News:

Printed Amstrad Addict magazine announced, check it out here!

Main Menu

Trying to create a filesystem ROM for Amstrad CPC but tape is still waiting

Started by kaoD, 19:54, 15 October 20

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

kaoD

Hi everyone! I'm new to the forum so please be gentle!

NOTE: This same question is posted in retrocomputing.se in case you want to answer there too for those juicy Internet Points.

NOTE 2: I haven't tried this on real hardware (can't pick up my 472 due to COVID-related issues), only on WinAPE, so if anyone wants to try I'm attaching the ROM in binary form. It's probably my fault and not the emulator's but you never know.

[attach=1][/attach]

Some background: I want to create a CPC MicroSD interface (inspired by @Duke's M4), so my first step is trying to hook the CAS* jumpblock entries just like AMSDOS and other DOSes do.

My first goal is to override CAS CATALOG so that Basic's CAT does not try reading from the cassette and instead prints something and returns immediately to Basic's prompt (just like CAT does when AMSDOS is loaded).

After inspecting Basic 1.0's listing (see D246 for Basic's CAT code) I discovered Basic also calls the CAS IN/OUT ABANDON functions (in D2AD) so I'll hook those too just in case.

I made a ROM (see the listing at the bottom of the question) that hooks the CAS CATALOG, CAS IN ABANDON and CAS OUT ABANDON functions and prints their names instead.

Everything seems to load and run fine (I'm using WinAPE), but when I type CAT in Basic, after the hooked CAS* functions are called (and their names are printed, so the hook worked)... the CPC is stuck reading from tape! I get no other message, and the only way to get the "Ready" message and keyboard response is to either press ESC to trigger a *break* or play a tape. Playing a tape from start to finish produces no output at all, but once finished cataloguing returns to Basic's prompt.

What am I doing wrong?

Here's AMSDOS which instantly goes back to Basic prompt (indicated by the Ready message) as I want mine to do:



And here's my ROM waiting for tape data:



My ROM after pressing ESC:



My ROM Listing

hello.rasm (works with RASM)


save "hello.rom",#c000,#ffff-#c000

org #c000

os_reset equ #0000
os_txt_output equ #bb5a
os_cas_in_abandon equ #bc7d
os_cas_catalog equ #bc9b
os_cas_out_abandon equ #bc92
kl_curr_selection equ #b912

header:
  db 1
  db 0,0,0
  dw rsx_commands

rsx_table:
  jp init
  jp hello
  jp reset

rsx_commands:
  str "TEST ROM"
  str "HELLO"
  str "RESET"
  db 0

ALIGN 2

init:
  push ix,bc

  ;; Reserve 9 bytes for RST 18h args
  ld bc, -9
  add hl, bc
  inc hl ;; HL = first available byte

  push af
    push hl ;; load hl into ix
    pop ix

    call kl_curr_selection ;; A = current upper ROM number

    ;; BB AA XX -- FAR CALL to AABB in ROM XX
    ld (ix+0), lo(my_cas_catalog)
    ld (ix+1), hi(my_cas_catalog)
    ld (ix+2), a
    ld (ix+3), lo(my_cas_in_abandon)
    ld (ix+4), hi(my_cas_in_abandon)
    ld (ix+5), a
    ld (ix+6), lo(my_cas_out_abandon)
    ld (ix+7), hi(my_cas_out_abandon)
    ld (ix+8), a
  pop af

  ;; Patch jump block entries with FAR CALL into our ROM
  ;; (HL) = DF LL HH = RST 18h (HHLL)
  ld bc, 3
  ld ix, os_cas_catalog
  ld (ix+0), #df
  ld (ix+1), l
  ld (ix+2), h
  add hl, bc
  ld ix, os_cas_in_abandon
  ld (ix+0), #df
  ld (ix+1), l
  ld (ix+2), h
  add hl, bc
  ld ix, os_cas_out_abandon
  ld (ix+0), #df
  ld (ix+1), l
  ld (ix+2), h
  ld bc, -6
  add hl, bc

  ;; Print copyright message
  push hl
    ld hl, str_init
    call print_str
  pop hl

  ;; HL = last available address for next ROM
  dec hl

  pop bc,ix

  ret

;; |HELLO
hello:
  push hl
    ld hl, str_hello
    call print_str
  pop hl
  ret

;; |RESET
reset:
  jp os_reset


;; ----

print_str:
    ld a, (hl)
  or a
  ret z
  call os_txt_output
  inc hl
  jr print_str

my_cas_catalog:
  push hl
    ld hl, str_cas_catalog
    call print_str
  pop hl

  ;; reset Z (preserving A)
  push bc
    ld b, a
    ld a, 1
    cp 0
    ld a, b
  pop bc

  ;; set carry flag
  scf

  ;; !Z && C == success
  ret

my_cas_in_abandon:
  ld hl, str_cas_in_abandon
  call print_str
  ret

my_cas_out_abandon:
  ld hl, str_cas_out_abandon
  call print_str
  ret

str_init: db " kaoD testing",10,13,10,13,0
str_hello: db "Hello world!",10,13,10,13,0
str_cas_catalog: db "CAS CATALOG",10,13,10,13,0
str_cas_in_abandon: db "CAS IN ABANDON",10,13,10,13,0
str_cas_out_abandon: db "CAS OUT ABANDON",10,13,10,13,0

GUNHED

Welcome and good luck with your interesting project.
http://futureos.de --> Get the revolutionary FutureOS (Update: 2023.11.30)
http://futureos.cpc-live.com/files/LambdaSpeak_RSX_by_TFM.zip --> Get the RSX-ROM for LambdaSpeak :-) (Updated: 2021.12.26)

cpcitor

Hi kaoD!

Congrats for the approach!

So, it looks like after calling *your* CATALOG routine, CPC calls the regular CATALOG routine?

Hmm, I see you've taken care of setting Carry to true, Zero to false, and even preserved the other registers, even though Soft968 says "BC, DE, HL, IX and other flags corrupt.". Same for CATALOG, IN ABANDON, OUT ABANDON.

What resource/doc did you base your code on?

I guess you already know this: http://cpctech.cpcwiki.de/docs/amsdos.asm
from http://cpctech.cpcwiki.de/docs.html

Don't despair, keep up the good work!
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.

pelrun

FYI the source to Duke's M4 rom is available at https://github.com/M4Duke/m4rom, so it's a good idea to check out how he's doing it.


(At the very least it'll give you a nicer way to clear the Z flag :D )

cpcitor

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.

kaoD

Thanks for the encouragement.

@cpcitor

QuoteSo, it looks like after calling *your* CATALOG routine, CPC calls the regular CATALOG routine?
I don't think it's calling the regular CATALOG routine since I'm not getting any messages (neither the "press PLAY" prompt, nor the actual cataloguing of each file). As I understand it the actual tape reading seems to still be in effect, since all input is ignored until I *break* or play a tape from start to finish. I'm not that well-versed into CPC's internals, but as I understand it the cassette reading is implemented in software... my guess is something (BASIC? the firmware?) must be calling SOME code that's still accessing SOME cassette-related routines.
I will try hooking more CAS* routines just in case I missed a call somewhere, or the firmware is calling something behind the scenes.

I'm normally testing without AMSDOS loaded, bare 464 OS ROM, but if I load AMSDOS alongside my ROM (AMSDOS at 7, mine at 1-6) the behavior is the same (waiting for tape).

QuoteHmm, I see you've taken care of setting Carry to true, Zero to false, and even preserved the other registers, even though Soft968 says "BC, DE, HL, IX and other flags corrupt.". Same for CATALOG, IN ABANDON, OUT ABANDON.
Indeed. Initially I didn't preserve anything but I added the code just in case it made any difference. It didn't :P

QuoteWhat resource/doc did you base your code on?

I've been reading so much I can't point to a single resource. I'm using Soft158 for the description of the firmware routines. I'm also reading BASIC 1.0 and AMSDOS listings to guide me on the debugger, but they quickly reach parts of the firmware I don't understand that well and didn't see anything out of the ordinary, so I have been unable to pinpoint the actual issue :/
Regarding the code, I'm just patching the FAR CALL in the jump table, but I can't remember where I took the idea from. The actual implementation is mine though, since code like M4's ROM took more complex approaches... I kept the code intentionally simple, so I was hoping I did something horribly stupid that was easy to point out xD

Thanks @pelrun and @cpcitor for the links! I've already visited some of them, but I will revisit them (and the new one) with fresh eyes.

pelrun

You're gonna kick yourself when you realise what the bug is: where do you think the CPC resumes after it executes the RST#18? Because there's no RET there...

pelrun

Sorry, I shouldn't be so coy: RST#18's not a jump, it's a call. So it returns to &BC9E, which is where the rest of the original CAS_CATALOG code is...

kaoD

LMAO!!
It's not calling the original CAS CATALOG routine... it's returning to BC9E which is... CAS WRITE's jump entry!!

:doh:

I tried just POPing the top of the stack as a quick fix, but that didn't work. AMSDOS uses FAR CALL for the patches (and it seems natural to me) so I'll see how it works!

Thank you so much for the pointer!

kaoD

Okay, so I've been thinking some more about this.

FAR CALL seems like it's the optimal solution since it's the only way to call an arbitrary upper ROM while only taking 3 RAM bytes per patch (the FAR CALL arg buffer) or even just 3 bytes in total if we use the AMSDOS trick. I see the M4 ROM uses this same AMSDOS trick (under the fio_jvec label which only now I realize means something along the lines of "Firmware IO Jump VECtor"...) but I'd like to keep it simple and patch each function individually for now.

The problem is FAR CALL stores the "bad" return address (the unwanted return to the CAS WRITE jump entry) on SP+6, so I can't just pop it. I've had success by manipulating the stack manually (sort of like fio_jvec does), but that looks like it depends too much on what RST 18h does with the stack before calling us.

So just to confirm, these are my alternatives:

1. Don't use FAR CALL and instead just jump

But this would require storing the routine in RAM (and will probably end up reimplementing a poor-man's version of FAR CALL).

2. Keep using FAR CALL (using the AMSDOS trick, or individual hooks) and manipulate the stack manually

I'm going to guess that, since AMSDOS and M4 use this, every known firmware implements FAR CALL as having the bad return address on SP+6. Is that right?

Is there any alternative I'm missing?

EDIT: I've added your answer as a retrocomputing.se answer so the information is there too. You probably don't care but just in case you preferred to post it yourself.

pelrun

Since there's not likely to be any new CPC firmware any time soon, it's pretty safe to assume that FAR CALL's internals will remain constant :D

CloudStrife

If you want more example you can look at to AlbiDOS source from OffseT: https://framagit.org/shinra/albireo/albireodos/-/tree/master (An opensource DOS for the open hardware Albireo SD/USB mass storage and serial interface card)

Lot of comment are in French, but you can ask OffseT on Freenode/#cpc about them :)

andycadley

Patch the firmware jump block entries with JMP instructions. They can jump to FAR CALLS, followed by RET which you store in the reserved RAM along with the far call addresses. That way most of the code can still exist in ROM.

cpcitor

Quote from: CloudStrife on 15:42, 16 October 20
If you want more example you can look at to AlbiDOS source from OffseT: https://framagit.org/shinra/albireo/albireodos/-/tree/master (An opensource DOS for the open hardware Albireo SD/USB mass storage and serial interface card)

Lot of comment are in French, but you can ask OffseT on Freenode/#cpc about them :)

I can translate French if Google translate is not enough.

Surprisingly enough, Google translate indeeds translates very well, even though all the letter with diacritics (accents) are just missing in the file (not the accents, the whole letters).

https://framagit.org/shinra/albireo/albireodos/-/tree/master/src
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