News:

Printed Amstrad Addict magazine announced, check it out here!

Main Menu
avatar_cpcitor

Triggering Amstrad BASIC/System errors from machine code

Started by cpcitor, 07:56, 23 September 24

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

cpcitor

Situation

I saw z80 - Triggering Amstrad BASIC/System errors from machine code - Retrocomputing Stack Exchange.

QuoteI'm writing some machine code that's intended to be executed from BASIC with a CALL and I'd like to trigger genuine errors just like running ERROR 5 (Improper argument) from BASIC.

I have the book The Ins and Outs of the Amstrad by Don Thomasson and I've looked at the jumpblock entries, but I didn't find it there.

How can I trigger errors from machine code that can be caught and handled by a BASIC program using ON ERROR GOTO?

I found this was an interesting small problem on its own.

Found a solution

It can be done by calling an appropriate location in ROM, but because the firmware does not provide a stable entry point for those features, the location in the ROM will depend on which version of the CPC the code runs (464, 664, 6128, plus).

; Assuming the wished error code is in register A
ld hl, # 0xCB55   ; for a 6128 or plus, or 0xCA93 for a 464 or 0xCB58 for a 664
ld c,  # 0xFD     ; means enable only upper ROM
jp     0x1B       ; see Soft968 section 18 e.g. https://www.cpcwiki.eu/imgs/7/7f/S968se18.pdf page 18.13

A fully working solution, including auto-detection of ROM version is on https://github.com/cpcitor/asm2err . Tested on emulated 464/664/6128/Plus. You can run it (on a Linux machine) with:

git clone https://github.com/cpcitor/asm2err
cd asm2err
make

One nice thing is that it allows you to raise and catch custom error numbers beyond the range 1-30 defined in the BASIC ROM.

Better solution?

Can you think of a different/better solution?
Had a CPC since 1985, currently software dev professional, including embedded systems.

I made in 2013 the first CPC cross-dev environment that auto-installs C compiler and tools: cpc-dev-tool-chain: a portable toolchain for C/ASM development targetting CPC, later forked into CPCTelera.

cpcitor

FWIW, I pushed an updated version, slightly shorter version by using a firmware call to enable upper ROM instead of doing it by hand, saves 9 bytes.
Calling KL CURR SELECTION then KL PROBE ROM was one byte longer (would allow more checks, but I guess they are not needed if the calling program is not in another ROM).

Possible code golf opportunity: rather than prepare and read a table, a few jumps might be shorter, as I saw in another thread.

Below from https://github.com/cpcitor/asm2err/blob/master/asm2err.s, slightly abridged.

.module asm2err

cpc_run_address::
    ; We start with a one-time configuration option.
jr_configure:
    jr configure        ; Once configured, jump destination adjusted to not configure again.
configure:
    push de            ; Save error number.

    ; Fetch byte at ROM 0xC002 of upper ROM, which is rom version number.
    call 0xB900        ; KL U ROM ENABLE
    ld hl, #0xC002
    ld c,(hl)
    ; call 0xB90C        ; KL ROM RESTORE ; can be skipped since firmware will take control anyway
    ; Ok ROM version number is in C.

    ; Compute address from where to pick value.
    ld b, #0
    ld hl, #table
    add hl, bc
    add hl, bc        ; HL points to correct entry in table.

    ld de, # where_hl_is_set+1

    ldi            ; Copy one byte.
    ldi            ; Copy second byte.

    ld hl, # jr_configure+1
    ld (hl), # raise_error - jr_configure - 2

    ; Configuration done!
    pop de            ; Restore error number.

    ; ----------------------------------------------------------------
    ; Actual work
    ; ----------------------------------------------------------------
raise_error:
    ; Preparing for "jp 0x1B" which assumes:
    ; - HL is set to an address (done above),
    ; - C specifies whether to enable upper or lower ROM
where_hl_is_set:
    ld hl, # 0x0000        ; Placeholder value, will be set by configuration on first run.
    ld c, #0xFD        ; Enable upper rom only.

    ld a, e            ; Get error number from CALL argument.
    jp 0x1B                ; Jump to address at HL, with ROM enable status as specified by C.

table:
.dw 0xCA93            ; Location on a CPC464, visible in e.g. "CPC 464 Intern" from Data Becker, page 366.
.dw 0xCB58            ; Location on a CPC664, obtained by looking in CPCEC's debugger.
.dw 0xCB55            ; Location on a CPC6128, see e.g. https://github.com/Bread80/Amstrad-CPC-BASIC-Source/blob/main/BASIC1.1.disasm#L2512
.dw 0xCB55            ; Does Basic ROM version 3 even exist?
.dw 0xCB55            ; Location on a CPC+, obtained by disassembling the ROM with z80dasm.

.area _DATA            ; To make linker happy
Had a CPC since 1985, currently software dev professional, including embedded systems.

I made in 2013 the first CPC cross-dev environment that auto-installs C compiler and tools: cpc-dev-tool-chain: a portable toolchain for C/ASM development targetting CPC, later forked into CPCTelera.

cpcitor

I have simplified code further. Given the BASIC context only optimizing for size should count. From the initial 58 bytes it comes down to 22 bytes.

Here it is in full:

.module asm2err

cpc_run_address::
    ; Error number comes in through DE.

    ; Fetch byte at ROM 0xC002 of upper ROM, which is rom version number.
    call 0xB900         ; KL U ROM ENABLE
    ld hl, #0xC002
    ld b,(hl)

    ; Firmware preserves E on call to 0xB900.

    ; Error number comes in through DE, set A to it.
    ld a,e            ; A = error number

    inc b
    djnz not_rom0_cpc464
    jp 0xca93        ; Location on a CPC464, visible in e.g. "CPC 464 Intern" from Data Becker, page 366.
not_rom0_cpc464:
    djnz not_rom1_cpc664
    jp 0xcb58        ; Location on a CPC664, obtained by looking in CPCEC's debugger.
not_rom1_cpc664:
    ; assume all ROMs are like BASIC 1.1. This is true for CPC+ models.
    jp 0xcb55        ; Location on a CPC6128, see e.g. https://github.com/Bread80/Amstrad-CPC-BASIC-Source/blob/main/BASIC1.1.disasm#L2512

.area _DATA            ; To make linker happy

Run it in 3 lines on Linux or similar (Windows Subsystem for Linux, Mac OS, Cygwin). Only Linux will run the emulator for you.

git clone https://github.com/cpcitor/asm2err
cd asm2err
make
Had a CPC since 1985, currently software dev professional, including embedded systems.

I made in 2013 the first CPC cross-dev environment that auto-installs C compiler and tools: cpc-dev-tool-chain: a portable toolchain for C/ASM development targetting CPC, later forked into CPCTelera.

Powered by SMFPacks Menu Editor Mod