Hi there!
I'm the developer of Turbo Rascal SE (see www.turborascal.com), an IDE + compiler + general toolshop for creating games/demos for various 8/16 bit computers. TRSE now also supports the Amstrad CPC 464, with a handful of tutorials (must use Caprice32 emulator, for now)
I'm currently adding more functionality to the CPC / Z80 stuff, and have recently implemented a resource compression/decompression system, working just fine.
Today, I implemented a system for automatic packing and extracting of executable files, but I'm having a huge problem - how the hell do I work with RAM at the Bxxx-range?
here is what I do :
- compile programs as usual in TRSE, but with executable packing toggled. This will generate code that starts at $200.- The compiled program is automatically packed, and then a *new* program is compiled - starting at $4000, containing the unpacking code + the packed executable.
- When the hacked code is executed on the CPC at $4000, it will first jump to the the unpacker code, copy itself to $100, jump there and start unpacking code/data to $200.
Everything works well for smaller codes - but whenever the unpacker starts reading data into the $B000-area, it crashes. I've tried experimenting with turning off ROM reading ie "call $B909, call $B903" but to no avail - everything seems to crash whenever $B??? is hit.
I am having a hard time finding useful information online about how to handle this - is there any way to turn *off* ROM/commands for $B000, so the that the packed code can be read from this location? Or am I missing something here?
Complete CPC n00b here, so any help would be appreciated!
I guess it's because thats the place of firmware jump blocks. You maybe have to disable firmware while doing that ?
http://www.cpcalive.com/docs/amstrad_cpc_6128_memory_map.html
Thanks for the reply, SRS! Problem is that I can't seem to find anything that resembles "disable firmware" etc at $B900 , only how to enable RAM for $C000-$FFFF. I'll keep looking though
CPCTELERA sources may help. https://github.com/lronaldo/cpctelera/tree/master/cpctelera/src/firmware
.include /firmware.s/
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; Function: cpct_disableFirmware
;;
;; Disables Amstrad CPC firmware, preventing it from being executed at every
;; CPU interrupt.
;;
;; C Definition:
;; u16 <cpct_disableFirmware> ()
;;
;; Assembly call:
;; > call cpct_disableFirmware_asm
;;
;; Return value:
;; <u16> - Pointer to present interrupt handler (normally, pointer to firmware ROM code).
;; This value should be stored to restore it later, if required.
;;
;; Details:
;; This function is exactly the same as <cpct_removeInterruptHandler>, as firmware
;; is executed as an interrupt handler, and disabling them is nothing more than removing
;; it. For more details on how it works, see <cpct_removeInterruptHandler>
;;
;; Disabling the firmware is useful for several reasons:
;; - Firmware code gets executed 6 times per frame (1 at each interrupt) when
;; is active. If you turn it off, you win CPU clock cycles for your program,
;; because firmware will not execute anymore.
;; - Most of CPCtelera's functions talk directly to hardware, no to firmware.
;; They are faster, but they do not change firmware variables. As a result,
;; firmware can revert changes made by CPCtelera's functions when active. For
;; instance, if you change to video mode 0 using CPCtelera's functions, firmware
;; will see that the video mode is different from what it should be (attending
;; to its own variables) and will change it again to 1. This happens with video
;; modes and palette values mainly.
;; - Also, firmware uses part of the RAM to store its variables (from
;; 0xA6FC to 0xBFFF). If you accidentaly overwrite anything there with firmware
;; being active, unpredictable things will happen, even hanging the computer.
;; Moreover, disabling firmware lets you use this part of the RAM for your
;; own uses.
;;
;; Destroyed Register values:
;; HL
;;
;; Required memory:
;; 16 bytes
;;
;; Time Measures:
;; (start code)
;; Case | microSecs (us) | CPU Cycles
;; --------------------------------------
;; Any | 22 | 88
;; --------------------------------------
;; (end code)
;;
;; Credits:
;; This function was initially based on cpc_disableFirmware function
;; from CPCRSLib by Raul Simarro.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; Function: cpct_removeInterruptHandler
;;
;; Sets nothing as interrupt handler (returns every time it is called). It
;; returns previous interrupt handler for restoration purposes.
;;
;; C Definition:
;; u16 <cpct_removeInterruptHandler> ()
;;
;; Assembly call:
;; > call cpct_removeInterruptHandler_asm
;;
;; Return value:
;; (u16, HL) - Previous interrupt code pointer located at 0x0039.
;;
;; Details:
;; This function sets Interrupt Mode to 1 (call 0x0038 every time a maskable
;; interrupt happens) and then writes assembly instructions EI : RET at 0x0038.
;; (0xC9FB --> 0xFB = EI, 0xC9 = RET). This makes the CPU directly return every
;; time an interrupt creates a jump to 0x0038 (6 times per frame, 300 times per
;; second).
;;
;; This function retrieves previous interrupt handler before setting it. This
;; previous interrupt handler is returned. You may store it as a 16-bits value
;; and use it again to restore it later on if you wanted.
;;
;; Modifying the interrupt vector, this function also disables firmware, as
;; firmware will not be called again. Firmware routines are executed as a interrupt
;; handler and the ROM entry point is stored at 0x0039 (a 16-bit address where the
;; firmware code starts).
;;
;; Before inserting 0xC9FB at 0x0038, the 2 bytes lying at 0x0039 are saved
;; into DE. These 2 bytes are returned to the caller, to let them be stored and
;; restored later on, if normal firmware operation (or previous interrupt handler)
;; is required again. <cpct_reenableFirmware> may be used for this restoring
;; operation.
;;
;; Destroyed Register values:
;; HL
;;
;; Required memory:
;; 16 bytes
;;
;; Time Measures:
;; (start code)
;; Case | microSecs (us) | CPU Cycles
;; -------------------------------------
;; Any | 22 | 88
;; -------------------------------------
;; (end code)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
_cpct_disableFirmware::
cpct_disableFirmware_asm::
_cpct_removeInterruptHandler::
cpct_removeInterruptHandler_asm::
di ;; [1] Disable interrupts
ld hl, (firmware_RST_jp+1) ;; [5] Obtain pointer to the present interrupt handler
ex de, hl ;; [1] DE = HL (DE saves present pointer to previous interrupt handler)
im 1 ;; [2] Set Interrupt Mode 1 (CPU will jump to 0x38 when a interrupt occurs)
ld hl, #0xC9FB ;; [3] FB C9 (take into account little endian) => EI : RET
ld (firmware_RST_jp), hl ;; [5] Setup new "null interrupt handler" and enable interrupts again
ei ;; [1]
ex de, hl ;; [1] HL = Pointer to previous interrupt handler (return value)
ret ;; [3] Return
;;-----------------------------LICENSE NOTICE------------------------------------
;; This file is part of CPCtelera: An Amstrad CPC Game Engine
;; Copyright (C) 2014 ronaldo / Fremos / Cheesetea / ByteRealms (@FranGallegoBR)
;;
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU Lesser General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU Lesser General Public License for more details.
;;
;; You should have received a copy of the GNU Lesser General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;;-------------------------------------------------------------------------------
;#####################################################################
;### MODULE: Firmware and ROM routines ###
;#####################################################################
;### Routines to disable CPC Firmware and reenable it when needed, ###
;### and managing Upper and Lower ROMs. ###
;#####################################################################
;
.module cpct_firmware
.include /firmware.s/
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; Function: cpct_reenableFirmware
;;
;; Re-enables previously disabled Amstrad CPC firmware.
;;
;; C Definition:
;; void <cpct_reenableFirmware> (<u16> firmware_ROM_pointer) __z88dk_fastcall;
;;
;; Assembly call:
;; > call cpct_reenableFirmware_asm
;;
;; Input Parameters (2 Bytes):
;; (2B HL) *firmware_ROM_pointer* - 2 bytes with previous pointer stored at 0x0039.
;; This is the address where firmware ROM code starts.
;;
;; Parameter Restrictions:
;; * *firmware_ROM_pointer* is a 16bits value that should have been previously obtained
;; calling <cpct_disableFirmware> or <cpct_removeInterruptHandler>. This 16bits value should
;; be the pointer to the firmware ROM code that must be called every time an interrupt happens.
;; This pointer is placed at 0x0039, along with a JP instruction (0xC3) at 0x0038. Being the
;; CPU in interrupt mode 1, a jump to 0x0038 address is produced 6 times per frame, 300
;; times per second.
;;
;; Details:
;; Restores normal operation of Amstrad CPC firmware after having been disabled.
;; Do not try to call this function before disabling firmware. If you do, the most
;; normal result is getting your Amstrad CPC resetted.
;;
;; This function could also be used to change the present interrupt handler. However,
;; take into account that it does not create a safe wrapper for the given interrupt
;; handler. Use it with care in this case.
;;
;; Destroyed Register values:
;; HL
;;
;; Required memory:
;; 11 bytes
;;
;; Time Measures:
;; (start code)
;; Case | microSecs(us) | CPU Cycles
;; -----------------------------------
;; Any | 16 | 64
;; -----------------------------------
;; (end code)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
.equ JP_opcode, 0xC3
_cpct_reenableFirmware::
cpct_reenableFirmware_asm::
di ;; [1] Disable interrupts
ld a, #JP_opcode ;; [2] A = 0xC3, opcode for JP instruction
ld (firmware_RST_jp), a ;; [4] Put JP instruction at 0x0038, to create a jump to the pointer at 0x0039
ld (firmware_RST_jp+1), hl ;; [5] HL = previous interrupt handler pointer (firmware ROM pointer)
ei ;; [1] Reenable interrupts and return
ret ;; [3] Return
;;-----------------------------LICENSE NOTICE------------------------------------
;; This file is part of CPCtelera: An Amstrad CPC Game Engine
;; Copyright (C) 2014 ronaldo / Fremos / Cheesetea / ByteRealms (@FranGallegoBR)
;;
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU Lesser General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU Lesser General Public License for more details.
;;
;; You should have received a copy of the GNU Lesser General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;;-------------------------------------------------------------------------------
;#####################################################################
;### MODULE: Firmware and ROM routines ###
;#####################################################################
;### Routines to disable CPC Firmware and reenable it when needed, ###
;### and managing Upper and Lower ROMs. ###
;#####################################################################
;
.module cpct_firmware
;;
;; Title: Firmware&ROM constants
;;
;;
;; Constants: Firmware useful constants
;;
;; Constants used by firmware routines.
;;
;; firmware_RST_jp - Memory address that stores a pointer to the start of
;; firmware code, executed on every interruption.
;; GA_port_byte - Output port where Gate Array (GA) listens.
;;
.equ firmware_RST_jp, 0x38 ;; Memory address were a jump (jp) to the firmware code is stored.
.equ GA_port_byte, 0x7F ;; 8-bit Port of the Gate Array
Thanks a bunch! That really helped, managed to both disable / enable firmware while copying.. but it still crashes, which I realized is probably due to the stack being located at $c000 and down! Do you guys know if there is a way to move the stack pointer (say, to $FF00)?
ah sorry, stupid question =) ld sp, $200