News:

Printed Amstrad Addict magazine announced, check it out here!

Main Menu

Interrupts and BASIC

Started by arnoldemu, 18:05, 02 June 09

Previous topic - Next topic

0 Members and 2 Guests are viewing this topic.

arnoldemu

We know that &0038 is called when an interrupt occurs when Z80 is in interrupt mode 1 (the default for CPC).

On some systems (e.g. C64 *spit*) you can normally poke the interrupt vector address to call your function first, and then you call the interrupt function itself. This is done so that you have control first.

I looked at doing the same on the CPC, because I wanted more stable mode changes, but this is not possible in BASIC.

Why?

BASIC uses a firmware function ("EDIT") &BD5E in CPC6128 firmware. This is used when you enter BASIC lines and also when you are typing in BASIC.

But, because this function is in the firmware and the firmware is active, this means the rom is paged into &0000-&3fff, so now the firmware is using the ROM version of &0038. So your interrupt will not be called at this time. I believe this will also effect BASIC'S INPUT.

So, your either forced to use firmwares EVENTs (which are actually quite good), or poke into the interrupt function in RAM (around &b941) and get it to redirect to your function first, then to go to the firmwares event functions.

Once I've got the code fully working I will put it up onto cpctech for others to look at.


My games. My Games
My website with coding examples: Unofficial Amstrad WWW Resource

AMSDOS

I really am an dunce when it comes to interrupts, I've been playing around with the example they have in the CPC6128 Manual (for DI) and I still don't get it!  ???


10 CLS:TAG:EVERY 10 GOSUB 90
20 X1=RND*320:X2=RND*320
30 Y=200+RND*200:C$=CHR$(RND*255)
40 FOR X=320-X1 TO 320+X2 STEP 4
50 DI
60 MOVE 320,0,1:MOVE X-2,Y:MOVE X,Y
70 PRINT " ";C$;:CALL &BD19
80 EI:NEXT:GOTO 20
90 MOVE 320,0:DRAW X+8,Y-16,0:RETURN


I can see the program seemly does two things cause of the Event routine, though removing them or leaving them there - I can't tell the difference! (Simply a case of not noticing the forest for the trees perhaps!).

Though why I was really writing (which is off-topic), regards Interrupt Handling. My Turbo Pascal 3 manual has this on Page 277 which kind of explains how things are in CP/M:

QuoteInterrupt Handling

The Turbo Pascal run time package and the code generated by the compiler are both fully interruptable. Interrupt service routines must preserve all registered used.

If required, interrupt service procedures may be written in Pascal. Such procedures should always by compiled with the A compiler directive active ({$A+}), they must not have parameters, and they must themselves insure that all registers used are preserved. This is done by placing an inline statement with the necessary PUSH instructions at the very beginning of the procedure, and another inline statement with the corresponding POP instructions at the very end of the procedure. The last instruction of the ending inline statement should be an EI instruction ($FB) to enable further interrupts. If daisy chained interrupts are used, the inline statement may also specify a RETI instruction ($ED,$4D), which will override the RET instruction generated by the compiler.

The general rules for register usage are that integer operations use only the AF, BC, DE, and HL registers, other operations may use IX and IY, and real opertations use the alternative registers.

An interrupt service procedure should not employ any I/O operations using the standard procedures and functions of TURBO Pascal, as these routines are not re-entrant. Also note that the BDOS calls (and in some instances BIOS calls, depending on the specific CP/M implementation) should not be performed from interrupt handlers, as these routines are not re-entrant.

The programmer may disable and enable interrupts throughout a program using DI and EI instructions generated by inline statements.

If mode 0 (IM 0) or mode 1 (IM 1) interrupts are employed, it is the responsibility of the programmer to initialize the restart locations in the base page (note that RST 0 cannot be used, as CP/M uses locations 0 through 7).

If mode 2 (IM 2) interrupts are employed, the programmer should generate an initialized jump table (an array of integers) at an absolute address, and initialize the I register through an inline statement at the beginning of the program.

Whenever I read this, I think of all those Firmware routines I'm using which don't have PUSHes or POPs around them, and this Reference Manual seems to stress out that protecting the values from things (like the firmware) which change the values within the registers. Personally though I haven't hit any problems by not using them.  Some of this other stuff is beyond me though, Interrupt Modes I have no concept of regarding them, I do recall seeing an Assembly program in AA which used one of the RST and it patch the address at 38 to the address of the routine so when a call from that particular RST was done it would run the simply "Hello World" program or something! :)
* Using the old Amstrad Languages :D   * with the Firmware :P
* I also like to problem solve code in BASIC :)   * And type-in Type-Ins! :D

Home Computing Weekly Programs
Popular Computing Weekly Programs
Your Computer Programs
Updated Other Program Links on Profile Page (Update April 16/15 phew!)
Programs for Turbo Pascal 3

arnoldemu

Quote from: CP/M User on 11:16, 25 January 11
I   really am an dunce when it comes to interrupts, I've been playing   around with the example they have in the CPC6128 Manual (for DI) and I   still don't get it!  ???
Basic interrupts are different to cpu/hardware interrupts.


EVERY 10 GOSUB 90


Means:
"Every 10 vsyncs that happen, gosub to line 90".

And the basic DI/EI stops BASICS "interrupt" mechanism. Think of these as a software made interrupt.

So BASIC interrupts are not as powerful as the hardware/z80 interrupts.

hardware/z80 interrupts are different.

There   are maskable (where the z80 can choose to ignore or accept interrupt   request), or non-maskable (where z80 has no choice but to accept).

Normally the hardware signals an interrupt request to the z80.
If   the maskable interrupt is enabled, then the z80 will finish the current   instruction, accept the interrupt and call the handler.

You can make the z80 ignore these interrupts with DI, and accept them with EI.
HALT   instruction can be used to wait until an interrupt request comes and   accept it (almost like stopping the z80 until one comes).

When the interrupt handler is called, maskable interrupts are ignored until you explicitly do an EI to accept them again.

In   the CPC, the gate-array generates the interrupts. it counts the hsyncs   from the crtc, so under normal crtc settings an interrupt comes every 52   lines.

The BASIC interrupt happens when a hardware interrupt comes and vsync is also active from crtc.

The firmware has "frame flyback" interrupt, which is derived in the same way. This is a software interrupt based off the hardware interrupt.

The "fast ticker" is based off the hardware interrupt. This is basically a hardware interrupt.

Now, the interrupt mode tells the z80 what to do when it accepts the interrupt.

Interrupt mode 1 makes the Z80 store PC on the stack, and then jump to &0038 (an RST location).

It executes the code here, normally a JP to the actual handler.

The handler must store any registers it changes, using POP and restore them before it ends the handler.
If   it doesn't it will break the registers used by the program that was   interrupted. The z80 only stores the PC register and nothing more.

Interrupt mode 2 is designed more for external interrupts from other hardware devices.
These   hardware devices *can* supply the z80 with a number at interrupt time,   and this directs the z80 to go to a different handler. The handlers are   defined in a table pointed to by I register.

Really though, when   used with the cpc, this value can end up being random, so you end up   using interrupt mode 2 a bit like interrupt mode 1, but you can then use   &0038 for your own need.


Things can get a bit more   complex when cpm and firmware is involved and external interrupts,but   it's best just to understand the main hardware interrupt first.
My games. My Games
My website with coding examples: Unofficial Amstrad WWW Resource

AMSDOS

Okay so BASIC's EI or DI are <> to EI or DI in Assembly.

I'm having trouble with the BASIC program I posted above (from the manual) cause I cannot find any noticable difference by including or excluding those BASIC EI or DI statements, however the original program (in the manual) is BASIC 1.1 and I simply replaced FRAME with CALL &BD19, cause I was lead to believe they do the same thing, or is that the mistake I'm making by assuming that, cause I know MC_FRAME_FLYBACK has some other uses, since one of my sound routines (in assembly) is using it to simply draw out the sound further as it would from a BASIC perspective.

I'm taking the term "Interrupt" literally by assuming it applies some kind of delay and that disabling it somehow it's like telling the Z80 to get a move on, though having it disabled means something else is compromised and are only asking for trouble if you want to use something which doesn't work while the interrupts are disabled and end up crashing the whole system. So I'm sort of thinking along the lines of if the Interrupts are disabled, the Firmware is a no go zone, switch it back on and their there - is that correct way of thinking about it if I was dealing with the Assembly "DI" & "EI"?

I had this other example from a loader I disassembled years ago and in that there's:

DI
LD HL,xxxx
LD DE,xxxx
LD BC,xxxx
LDIR


From memory I think what it would do after that is execute the game, though it would be from a game where it would load the main program and then load some extra code and that code would move it to an area in memory perhaps where AMSDOS was situated in memory. Would that be the main reason for Disabling Interrupts if that was the case?
* Using the old Amstrad Languages :D   * with the Firmware :P
* I also like to problem solve code in BASIC :)   * And type-in Type-Ins! :D

Home Computing Weekly Programs
Popular Computing Weekly Programs
Your Computer Programs
Updated Other Program Links on Profile Page (Update April 16/15 phew!)
Programs for Turbo Pascal 3

redbox

#4
Quote from: CP/M User on 23:09, 25 January 11
I simply replaced FRAME with CALL &BD19, cause I was lead to believe they do the same thing, or is that the mistake I'm making by assuming that, cause I know MC_FRAME_FLYBACK has some other uses, since one of my sound routines (in assembly) is using it to simply draw out the sound further as it would from a BASIC perspective.


FRAME and CALL &BD19 do the same thing, wait for the frame flyback.  What happens here is the screen is drawn 50 times a second (50hz) from top to bottom.  If you run some code in a loop and that code takes 1/2 the time it takes to draw the screen (or frame) to run, then by calling MC_FRAME_FLYBACK you are waiting for the other half of the screen to be drawn until you run your code again.  So by doing this you are keeping everything nice and uniform, which is helpful for programming just about anything - games, demos and indeed your sound routines.

Quote from: CP/M User on 23:09, 25 January 11

I'm taking the term "Interrupt" literally by assuming it applies some kind of delay and that disabling it somehow it's like telling the Z80 to get a move on, though having it disabled means something else is compromised and are only asking for trouble if you want to use something which doesn't work while the interrupts are disabled and end up crashing the whole system. So I'm sort of thinking along the lines of if the Interrupts are disabled, the Firmware is a no go zone, switch it back on and their there - is that correct way of thinking about it if I was dealing with the Assembly "DI" & "EI"?


An easy way to think of an interrupt is it being something that happens at the same time during every frame (at 50hz as described above).  So if you are running some code, you can put an interrupt in the frame - for example, you could say "when the screen drawing reaches line 100, stop what you're currently doing and run this code" - and this would be an interrupt.  This is of course useful in some cases as you can ensure something happens at exactly the same time every frame, but also can be a pain because the CPC itself uses interrupts to do certain things and these could interfere with the code you're running (hence why you might want to Disable them).  Of course the downside is that with interrupts disabled, you can't use the firmware.

I think the DI in your code example is not really relevant - all the code there is doing is copying a block of memory from one place (HL) to another (DE) of a certain size (BC).  This is usually done at the beginning of a program (to copy graphics to the screen ram for example) and if the programmer wants interrupts disabled they also usually do it at the beginning of the code - so it's probably just co-incidence that you came across it here.

arnoldemu

Quote from: CP/M User on 23:09, 25 January 11
Okay so BASIC's EI or DI are <> to EI or DI in Assembly.
Yes, their idea is the same, but functionally they don't do the same thing


Quote from: CP/M User on 23:09, 25 January 11
I'm having trouble with the BASIC program I posted above (from the manual) cause I cannot find any noticable difference by including or excluding those BASIC EI or DI statements, however the original program (in the manual) is BASIC 1.1 and I simply replaced FRAME with CALL &BD19, cause I was lead to believe they do the same thing, or is that the mistake I'm making by assuming that, cause I know MC_FRAME_FLYBACK has some other uses, since one of my sound routines (in assembly) is using it to simply draw out the sound further as it would from a BASIC perspective.
these are the same.


Quote from: CP/M User on 23:09, 25 January 11
  I'm taking the term "Interrupt" literally by assuming it applies some kind of delay and that disabling it somehow it's like telling the Z80 to get a move on, though having it disabled means something else is compromised and are only asking for trouble if you want to use something which doesn't work while the interrupts are disabled and end up crashing the whole system. So I'm sort of thinking along the lines of if the Interrupts are disabled, the Firmware is a no go zone, switch it back on and their there - is that correct way of thinking about it if I was dealing with the Assembly "DI" & "EI"?
It's not a delay.
Think that your program is doing something, the interrupt comes, your program is stopped. Now the code for handling the interrupt is processed. Now once that returns, your code continues.

Redbox is also correct, think of them as a way of doing something at a specific time on the screen.

In the basic code, I don't think the basic "interrupt" is having chance to trigger that basic code.


Quote from: CP/M User on 23:09, 25 January 11
    I had this other example from a loader I disassembled years ago and in that there's:

DI
LD HL,xxxx
LD DE,xxxx
LD BC,xxxx
LDIR


From memory I think what it would do after that is execute the game, though it would be from a game where it would load the main program and then load some extra code and that code would move it to an area in memory perhaps where AMSDOS was situated in memory. Would that be the main reason for Disabling Interrupts if that was the case?
You need it if you are about to overwrite memory in the range &0000-&0038 or &a600-&c000.
The firmware interrupt handler is around &b900 or so.
Firmware has variables around &b000, and amsdos has them around &a600.

So by disabling the interrupt you are free to write over the firmware jumpblocks etc.
My games. My Games
My website with coding examples: Unofficial Amstrad WWW Resource

redbox

Quote from: arnoldemu on 10:26, 26 January 11
So by disabling the interrupt you are free to write over the firmware jumpblocks etc.


Good point, forgot about that one  ;)

AMSDOS

Okay, I wasn't thinking of it as a deliberate delay, more along the lines of your program is doing something and an interrupt comes along and stops until that Interrupt is processed.

One of the routines which I posted in another thread allows a series of lines which are drawn from a place in memory, though in order to have a loop drawing those line points, the program has to point to where that array is in memory and poke the values into a spot in memory where something like a GRA LINE ABSOLUTE can draw them, so lots of LD backwards and fowards before it finally calls that Firmware instruction. I was just wondering if Disabling Interrupts would have any benefit in that and then Enabling it before the firmware call is made?
* Using the old Amstrad Languages :D   * with the Firmware :P
* I also like to problem solve code in BASIC :)   * And type-in Type-Ins! :D

Home Computing Weekly Programs
Popular Computing Weekly Programs
Your Computer Programs
Updated Other Program Links on Profile Page (Update April 16/15 phew!)
Programs for Turbo Pascal 3

Powered by SMFPacks Menu Editor Mod