Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Interrupt Mode 2
#1
So... if I was crazy and wanted to implement an IM2 routine.... how do you think I should do it?

Most of my attempts to put the 257 byte data block in memory have failed spectacularly. I think using ORG in an asm statement tends to upset the compiler; but the thing about setting up interrupts is we NEED to put a 257 byte vector table into a very very specific location in memory, and then need to ALSO have the actual interrupt service routine in the specific place that the vector table points to.

*ponder*

I suppose we could use DIM variable @address to force the issue with an array?

On the whole though, this fixed location code isn't really supported by the compiler at this time. How could we work around it?
Reply
#2
Thinking about it some more. If we assume the programmer is smart enough to NOT use the last three pages of memory, we can probably use them for IM2 purposes.

The program below is far from stable. It crashes eventually, depending what you try to do. It's probably because it tries to go back to basic, which we probably don't want to generally. That or I forgot some registers or something.

It's interesting to see it print in the middle of the screen and refuse to move it though!

Code:
sub im2Initialize()
DIM addr as uInteger

FOR addr=$FD00 TO $FE00
poke addr,$FE
NEXT addr

POKE $FEFE,243 '; di
POKE $FEFF,205 '; CALL
POKE Uinteger $FF00,@isr
POKE $FF02,251 '; ei
POKE $FF03,$ED
POKE $FF04,77 '; ED4D reti

asm
LD A,$FD
LD I,A
IM 2
end asm

return

isr:
asm
;This is the interrupt Service Rotuine
; First thing, save those registers.
PUSH AF
PUSH BC
PUSH DE
PUSH HL
PUSH IX
EXX
PUSH AF
PUSH BC
PUSH DE
PUSH HL
end asm

Print at 10,10;"Ha!"

asm
POP HL
POP DE
POP BC
POP AF
EXX
POP IX
POP HL
POP DE
POP BC
POP AF

; This jumps to the ROM ISR.
call $0038
ret
end asm
end sub

PRINT "Hello world"
im2Initialize()
Reply
#3
How adorable is this?

http://britlion.googlepages.com/Sprity.z80
Reply
#4
That's beyond my awasomeness already. I swear I knew enough about coding to understand what it does (and how!). So I need to study more Smile
Reply
#5
Ironically, once I'd worked out how to make the compiler do an interrupt mode 2 handler, getting your demo to run was scarily easy.

The IM2 code gets called once per interrupt, and hops over to do one run around what used to be the main loop.

So the main loop was renamed with a label instead of "DO" and has a return at the end of it. It's called with a gosub once per interrupt.

That's the whole trick.
Reply
#6
:o Wooow!

Don't know whether you use ZX BASIC to compile that. If you did, I guess -O3 won't work with this, as it won't probably understand IM2 routines, etc...
Again, astonishing!!!
Reply
#7
boriel Wrote::o Wooow!

Don't know whether you use ZX BASIC to compile that. If you did, I guess -O3 won't work with this, as it won't probably understand IM2 routines, etc...
Again, astonishing!!!

I was wondering when you'd notice that little gem of fun. Yes, it's ALL done with ZX Basic and as for -O3, I actually didn't, because I've been avoiding use of O3 etc - or was at that time; you want us to test it now :-)

It shouldn't make a difference, I think, because I sorta used a hammer to make it work. I used the routine listed above - which pokes the IM2 system into place the hard way. I couldn't find a way to let it allow me to put code where I wanted any other way.

Here's the actual whole program (and I know the IM2 bit isn't as efficient as it could be, it was a first pass, and hey, it worked):
Code:
#DEFINE BLACK 0
#DEFINE BLUE 1
#DEFINE RED 2
#DEFINE MAGENTA 3
#DEFINE GREEN 4
#DEFINE CYAN 5
#DEFINE YELLOW 6
#DEFINE WHITE 7
#DEFINE TRANSPARENT 8
#DEFINE CONTRAST 9
#DEFINE TRUE 1
#DEFINE FALSE 0


sub im2Initialize()
DIM addr as uInteger

FOR addr=$FD00 TO $FE00
poke addr,$FE
NEXT addr

POKE $FEFE,243 '; di
POKE $FEFF,205 '; CALL
POKE Uinteger $FF00,@isr
POKE $FF02,251 '; ei
POKE $FF03,$ED
POKE $FF04,77 '; ED4D reti

asm
LD A,$FD
LD I,A
IM 2
end asm

return

isr:
asm
;This is the interrupt Service Rotuine
; First thing, save those registers.
PUSH AF
PUSH BC
PUSH DE
PUSH HL
PUSH IX
EXX
PUSH AF
PUSH BC
PUSH DE
PUSH HL
end asm

gosub mainLoop

asm
POP HL
POP DE
POP BC
POP AF
EXX
POP IX
POP HL
POP DE
POP BC
POP AF

; This jumps to the ROM ISR.
call $0038
ret
end asm
end sub






SUB fspInitialize (n as uByte, udgA as uByte, udgB as uByte, udgC as uByte, udgD as uByte,x as uByte,y as uByte)  'Initialize Sprite: n (sprite number 0-3);a,b,c,d (UDG number 0-20,99 means the sprite won't be printed)
#ifndef FINAL_CODE
IF n>3 OR (udgA>20 AND udgA<>99) or udgB>20 or udgC>20 or udgD>20 then Print "Out Of Bounds Call in ";"fspInitialize" : stop : END IF : REM Bounds checking - so a bad function call doesn't poke all over memory.
#endif
DIM targetAddr as uInteger
targetAddr=@fspDataStart+(48*n)
fspErase()
POKE targetAddr,udgA
targetAddr=targetAddr+1
POKE targetAddr,udgB
targetAddr=targetAddr+1
POKE targetAddr,udgC
targetAddr=targetAddr+1
POKE targetAddr,udgD
targetAddr=targetAddr+1
POKE targetAddr,x
targetAddr=targetAddr+1
POKE targetAddr,y
targetAddr=targetAddr+1
POKE targetAddr,x
targetAddr=targetAddr+1
POKE targetAddr,y
fspBufferAndDraw()
RETURN

fspDataStart:
REM This Section contains Fourspriter data and code that is called by other routines.

ASM
;; 16x16 Sprites moving a character each frame
;; Copyleft 2009 The Mojon Twins.
;; Uses UDGs (it reads the address from the system variables).
;; It moves up to 4 sprites, preserving the backgrounds.

;; Sprite control is done writing a certain number
;; in the Sprite's first UDG
;; If the first UDG is 99, it won't be printed

; Note that the sprites are initialized to disabled.

datap:                        ; each block is 48 bytes long. [This comment originally said 40 bytes, and it's clearly 48]
;; Sprite 1
udgs1:      defb   99,0,0,0      ; Four UDGs of the first sprite.
x_pos1:     defb   0            ; X position in chars.
y_pos1:     defb   0            ; Y position in chars.
cx_pos1:    defb   0            ; Previous X position in chars.
cy_pos1:    defb   0            ; Previous Y position in chars.
buffer1:    defb   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0         ; Background Gfx buffer.
attrs1:     defb   6,7,7,7      ; Sprite ATTRs
buffatrs1:  defb   0,0,0,0      ; Background Attr's buffer

;; Sprite 2
udgs2:      defb   99,0,0,0
x_pos2:     defb   0
y_pos2:     defb   0
cx_pos2:    defb   0
cy_pos2:    defb   0
buffer2:    defb   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
attrs2:     defb   7,7,7,7
buffatrs2:  defb   0,0,0,0

;; Sprite 3
udgs3:      defb   99,0,0,0
x_pos3:     defb   0
y_pos3:     defb   0
cx_pos3:    defb   0
cy_pos3:    defb   0
buffer3:    defb   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
attrs3:     defb   7,7,7,7
buffatrs3:  defb   0,0,0,0

;; Sprite 4
udgs4:      defb   99,0,0,0
x_pos4:     defb   0
y_pos4:     defb   0
cx_pos4:    defb   0
cy_pos4:    defb   0
buffer4:    defb   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
attrs4:     defb   7,7,7,7
buffatrs4:  defb   0,0,0,0

;; General use coordinates
xpos:      defb    0
ypos:      defb    0
cxpos:     defb   0
cypos:     defb    0


fspCopyToScreen:
        
         ld b,(xpos)
         ld c,(ypos)
        
        
         call   get_scr_address2   ; Returns (xpos, ypos) in HL
         ld      a,   (de)
         ld      (hl), a
         inc      h
         inc      de
         ld      a,   (de)
         ld      (hl), a
         inc      h
         inc      de
         ld      a,   (de)
         ld      (hl), a
         inc      h
         inc      de
         ld      a,   (de)
         ld      (hl), a
         inc      h
         inc      de
         ld      a,   (de)
         ld      (hl), a
         inc      h
         inc      de
         ld      a,   (de)
         ld      (hl), a
         inc      h
         inc      de
         ld      a,   (de)
         ld      (hl), a
         inc      h
         inc      de
         ld      a,   (de)
         ld      (hl), a
         inc      h
         inc      de
        
         ret
        
        
         fspCopyToScreen2:
        
         call   get_scr_address2   ; Returns (xpos, ypos) in HL
         ld      a,   (de)
         ld      (hl), a
         inc      h
         inc      de
         ld      a,   (de)
         ld      (hl), a
         inc      h
         inc      de
         ld      a,   (de)
         ld      (hl), a
         inc      h
         inc      de
         ld      a,   (de)
         ld      (hl), a
         inc      h
         inc      de
         ld      a,   (de)
         ld      (hl), a
         inc      h
         inc      de
         ld      a,   (de)
         ld      (hl), a
         inc      h
         inc      de
         ld      a,   (de)
         ld      (hl), a
         inc      h
         inc      de
         ld      a,   (de)
         ld      (hl), a
         inc      h
         inc      de
        
         ret
        
; This function returns the address into HL of the screen address
; (xpos, ypos) in character grid notation.
; Original code was extracted by BloodBaz

get_scr_address:
         push    de
         ld       a,   (xpos)
         and      31
         ld      l,   a
         ld      a,   (ypos)
         ld      d,   a
         and      24
         add      a,   64
         ld      h,    a
         ld      a,   d
         and      7
         rrca
         rrca
         rrca
         or      l
         ld      l,    a
         pop      de
         ret
              
; This function returns the address into HL of the screen address
; (xpos, ypos) in character grid notation.
; Original code was extracted by BloodBaz

get_scr_address2:
         push    de
         ld       a,b ; b=x
         and      31
         ld      l,   a
         ld      a,c ; c=y
         ld      d,   a
         and      24
         add      a,   64
         ld      h,    a
         ld      a,   d
         and      7
         rrca
         rrca
         rrca
         or      l
         ld      l,    a
         pop      de
         ret
              
              
;; This function returns the memory address of the Character Position
;; (xpos, ypos) in the attribute screen memory.
;; Adapted from code by Jonathan Cauldwell.

get_attr_address:
        
         ld      a,    (ypos)      ; Cogemos y
         rrca
         rrca
         rrca               ; Multiply by 32
         ld      l,   a     ; Pass to L
         and      3         ; Mask with 00000011
         add      a,   88   ; 88 * 256 = 22528 - start of attributes.
         ld      h,   a     ; Put it in the High Byte
         ld      a,   l     ; We get y value *32
         and      224       ; Mask with 11100000
         ld      l,   a     ; Put it in L
         ld      a,   (xpos); Grab X
         add      a,    l   ; Add it to the Low byte
         ld      l,   a     ; Put it back in L, and we're done. HL=Address.
         ret
        
get_attr_address2:
         ld      a,c    ;(ypos)      ; Cogemos y
         rrca
         rrca
         rrca               ; Multiply by 32
         ld      l,   a     ; Pass to L
         and      3         ; Mask with 00000011
         add      a,   88   ; 88 * 256 = 22528 - start of attributes.
         ld      h,   a     ; Put it in the High Byte
         ld      a,   l     ; We get y value *32
         and      224       ; Mask with 11100000
         ld      l,   a     ; Put it in L
         ld      a,b;   (xpos); Grab X
         add      a,    l   ; Add it to the Low byte
         ld      l,   a     ; Put it back in L, and we're done. HL=Address.
         ret


spare:      defb   85,85,85,85,85,85,85,85


;; Routine to save the background to the buffer
fspEraseAsm:

         ld       hl, datap      ;
         ld      b,   4         ;
i4chars2:   push   bc
        
         ;;
         ld      a,   (hl)
         cp      99
         jr      z,   nxt2

         ;; A
         LD BC,6
         ;EX DE,HL ; HL now points at the start sprite data instead
         ADD HL,BC  ; HL moved on to point to old x-y positions.
        
         LD b,(HL) ; b=x
         INC HL
        
         LD c,(HL) ;c=y
         INC HL
        
         EX DE,HL ; Now it's DE that points into the data buffer.
      
         ; Draw first background character
         call   fspCopyToScreen2
        
         ;; B
         ; xpos++
         inc b
        
         ; Draw second background Character
         call   fspCopyToScreen2
  
         ;; C
         ; xpos --
         dec b
         ; ypos ++
         inc c
         ; draw Third background Character
         call    fspCopyToScreen2

         ;; D
         ; xpos++
         inc b
         ; Draw Fourth background Character
         call   fspCopyToScreen2
        
         ;; move to attr
         inc      de
         inc      de
         inc      de
         inc      de
        
         ;Set our position to top left corner again
         ; xpos --
         dec b
         ; ypos --
         dec c
        
         call   get_attr_address2
         call   copyattrs
         EX DE,HL
nxt2:    pop      bc
         djnz    i4chars2        
      
         ret


fspCopyFromScreen:
         call   get_scr_address2
         ld      a,   (hl)
         ld      (de), a
         inc      h
         inc      de
         ld      a,   (hl)
         ld      (de), a
         inc      h
         inc      de
         ld      a,   (hl)
         ld      (de), a
         inc      h
         inc      de
         ld      a,   (hl)
         ld      (de), a
         inc      h
         inc      de
         ld      a,   (hl)
         ld      (de), a
         inc      h
         inc      de
         ld      a,   (hl)
         ld      (de), a
         inc      h
         inc      de
         ld      a,   (hl)
         ld      (de), a
         inc      h
         inc      de
         ld      a,   (hl)
         ld      (de), a
         inc      h
         inc      de
        
         ret  

fspBufferAndDrawAsm:

         ld     hl, datap      ;
         ld     b,   4         ;
i4chars:
         push   bc
        
         ;Active Sprite?
         ld      a,   (hl)
         cp      99
         jr      z,   nxt1

         ;; A
         LD BC,4
         ADD HL,BC ; move past the udg part
        
         ;xpos
         ld b,(hl)
         inc hl
        
         ;ypos
         ld c,(hl)
         inc hl
        
         ld a,b
         ld (xpos),a
         ld a,c
         ld (xpos),a    ; we won't need this soon.
        
                  
         inc    hl   ; move past the old co-ordinates
         inc    hl
         EX de,hl
         call   fspCopyFromScreen
        
         ;; B
         ; xpos++
         inc b
         call    fspCopyFromScreen
        
         ;; C
         ; xpos --
         dec b
         ; ypos ++
         inc c
         call    fspCopyFromScreen
        
         ;; D
         ; xpos++
         inc b
         call   fspCopyFromScreen
        

         ;; Now we point to the ATTR buffer,adding 4:
         inc      de
         inc      de
         inc      de
         inc      de
        
         ;Put ourselves back at the top left corner.
         ; xpos --
         dec b
         ; ypos --
         dec c      
        
        
         call   get_attr_address2
         ld      bc, 32
        
         ;First Character
         LDI  ; bc was loaded with 32 instead of 31 to account for the BC-- this instruction has.
        
         ;Second Character
         LDI
         add      hl,   bc ; we can get away with this because BC++ as well as hl--
        
         ;Third Character
         LDI
                                
         ;Fourth Character
         LDI
        
         EX DE,HL ; back to an HL pointer.
nxt1:    pop      bc
         djnz    i4chars
         ; Go straight into Painting new sprites for speed.

        

;; Print sprites routine
;; UDGs are labeled from  0 to 21. The addres of the UDG is:
;; *(23675) + 256 * *(23676) + 8 * N
fspDrawAsm:
draw_sprites:
         ld      hl, datap      ;
         ld      b,   4         ;
i4chars3:   push   bc
         ld      a,   (hl)
         cp      99
         jr      z,   nxt3
        
         ;; Copy pointer "HL" to alt reg
         push HL
         exx
         pop HL
         exx
                  
         ld bc,4
         add hl,bc
        
         ; Get xpos
         LD a,(hl)
         ld b,a ;x
         inc hl
        
         ; Get ypos
         LD a,(hl)
         ld c,a   ;y
         inc hl
        
         push   HL ;  de  Save pointer.
        
         EXX
         LD A,(HL)
         INC HL
         EXX
        
         call   fspGetUdgAddr
         ;Draw the first character
         call   fspCopyToScreen2
         ; xpos++
         inc b
        
         ; Address of the graphics
         EXX
         LD A,(HL)
         INC HL
         EXX
         call   fspGetUdgAddr
        
         ; Draw the second Character
         call   fspCopyToScreen2
        
         ; xpos--
         dec b
        
         ; ypos ++
         inc c
        
         ; Address of Graphics
         EXX
         LD A,(HL)
         INC HL
         EXX
         call   fspGetUdgAddr
        
         ; Draw the third character
         call   fspCopyToScreen2
        
         ; xpos ++
         inc b
        
         ; Address of next Graphic
         EXX
         LD A,(HL)
         INC HL
         EXX
         call    fspGetUdgAddr
        
         ; Draw the fourth Character
         call   fspCopyToScreen2
                    
         pop      hl   ; Recover hl to point at the start of the pixel buffer
        
         ; hl = hl + 32
         ld a,l
         add a,34
         ld l,a
         jp nc, fspNoIncH
         inc h    ; hl now points to the attr data.
fspNoIncH:        
         EX DE,HL
        
         ;Reset position to top left.
         ; xpos --
         dec b
         ; ypos --
         dec c
        
         ;; attr
         call   get_attr_address2
         call   copyattrs
         EX DE,HL
        
         LD BC,4
         ADD HL,BC ; Move pointer to start of next block
        
nxt3:    pop      bc
         djnz    i4chars3
        
         ret


        
fspGetUdgAddr:  
         ; finds the address of the UDG # in A
         ld HL,(23675)
         rlca
         rlca
         rlca   ; a = N * 8
         ld      d, 0
         ld      e, a
         add      hl, de
         EX DE,HL
         ret
                  
copyattrs:  
         ld      bc, 32
         EX DE,HL
        
         ;First
         LDI
         ;Second
         LDI
        
         ; DE=DE+BC
         EX DE,HL
         add     hl,bc
         EX DE,HL
        
         ;Third
         LDI
                  
         ;Fourth
         LDI
        
         EX DE,HL
         ret
        

fspUpdateAsm:          ; This name will be used from ASM
update_coordinates:
        ; For each Sprite:
         ;; *(datap + 6) = *(datap + 4)
         ;; *(datap + 7) = *(datap + 5)

         ld     hl, datap+4     ; Points to sprite 1
         ld     de, datap+6            
         ldi
         ldi

         ld     hl, datap+4+48     ; Points to sprite 2
         ld     de, datap+6+48                  
         ldi
         ldi

         ld     hl, datap+4+48+48     ; Points to sprite 3
         ld     de, datap+6+48+48                
         ldi
         ldi

         ld     hl, datap+4+48+48+48     ; Points to sprite 4
         ld     de, datap+6+48+48+48                    
         ldi
         ldi
         ret
END ASM
END SUB

SUB fspDisable (n as UByte)
#ifndef FINAL_CODE
IF n>3 then Print "Out Of Bounds Call in ";"fspDisable" : stop: END IF
#endif
POKE @fspDataStart+48*n,99
end SUB

SUB fspCoord (n as uByte, x as uByte, y as uByte)   'Set sprite coords: n (sprite number);x,y (vertical,horizontal coords)
#ifndef FINAL_CODE
IF n>3 then Print "Out Of Bounds Call in ";"fspCoord" : stop: END IF
#endif
DIM targetAddr as uInteger
targetAddr=@fspDataStart+4+48*n
POKE targetAddr,x
targetAddr=targetAddr+1
POKE targetAddr,y
rem targetAddr=targetAddr+1
rem POKE targetAddr,x
rem targetAddr=targetAddr+1
rem POKE targetAddr,y
END SUB

SUB fspAttrs (n as uByte, attra as uByte, attrb as uByte, attrc as uByte, attrd as uByte) 'Set sprite attrs: n (sprite number);a,b,c,d (UDG attrs)
#ifndef FINAL_CODE
IF n>3 then Print "Out Of Bounds Call in ";"fspAttrs" : stop: END IF
#endif

DIM targetAddr as uInteger
targetAddr=@fspDataStart+40+48*n
POKE targetAddr,attra
targetAddr=targetAddr+1
POKE targetAddr,attrb
targetAddr=targetAddr+1
POKE targetAddr,attrc
targetAddr=targetAddr+1
POKE targetAddr,attrd
END SUB

FUNCTION fspAttrByte(fspInk as uByte,fspPaper as uByte,fspBright as uByte, fspFlash as uByte) as uByte
#ifndef FINAL_CODE
    if fspInk > 7 OR fspPaper > 7 OR fspBright > 1 or fspFlash >1 then Print "Out Of Bounds Call in ";"fspAttrByte" : stop: END IF
#endif
    return (fspFlash shl 7) + (fspBright shl 6) + (fspPaper shl 3) + fspInk
END FUNCTION

SUB fspAttr(n as uByte,fspInk as uByte,fspPaper as uByte,fspBright as uByte, fspFlash as uByte)
#ifndef FINAL_CODE
    if fspInk > 7 OR fspPaper > 7 OR fspBright > 1 or fspFlash >1 then Print "Out Of Bounds Call in ";"fspAttr" : stop: END IF
#endif
    DIM attrByte as uByte
    attrByte=fspAttrByte(fspInk,fspPaper,fspBright,fspFlash)
    fspAttrs(n,attrByte,attrByte,attrByte,attrByte)
END SUB    

SUB FASTCALL fspErase()
  asm
  call fspEraseAsm
  end asm
END SUB


SUB FASTCALL fspBufferAndDraw()
asm
call fspBufferAndDrawAsm
end asm
END SUB

SUB FASTCALL fspUpdate()
asm
call fspUpdateAsm
END ASM
END SUB


SUB fspRedraw()
asm
   ;halt we're in IM2, so no halting. NO! BAD!
   ; Wait for scan line
   LD BC,1930 ; Reduced this to the bare minimum to allow scanline to hit bottom of screen. Your program may need more or less.
   fspRedrawloop:
   DEC BC
   LD A,B
   OR C
   JR NZ,fspRedrawloop
   ;REM Erase the sprites
   call fspEraseAsm
   ;REM Save background and
   ;REM print sprites
   call fspBufferAndDrawAsm
   ;REM update coordinates
   call fspUpdateAsm
  
end asm
END SUB


REM below this is Apenao's Demo Program:




#include <sinclair.bas>
#include <keys.bas>


100 DIM gentle (0 to 3,0 to 7) AS uByte => {  { 15, 15, 15, 15, 15, 15, 13, 15} , _
     { 240, 144, 208, 208, 240, 240, 176, 240} , _
     { 15, 14, 63, 0, 0, 12, 26, 30} , _
     { 176, 112, 252, 0, 0, 48, 104, 120}}

110 POKE Uinteger 23675, @gentle(0,0)

    CLS
    'for n = 0 to 15
    'print paper (n mod 7); ink ((n+3) mod 8); "ABCDEFGHIJKLMNOPQRSTUVWXYZ123456"
    'next n
    
    LET gx=10:LET gy=10:let dx=3:let dy=3:let tx=6:let ty=6:let cx=18:let cy=18
    let dmx=1:let dmy=1:let tmx=-1:let tmy=-1:let cmx=1:let cmy=-1
    fspInitialize (0,0,1,2,3,gx,gy) : REM (sprite number, udgA, udgB,udgC,udgD, initial X co-ordinate, initial y-coordinate)                                                      
    fspInitialize (1,0,1,2,3,dx,dy)
    fspInitialize (2,0,1,2,3,tx,ty)
    fspInitialize (3,0,1,2,3,cx,cy)
    
    fspAttr(0,WHITE,MAGENTA,FALSE,FALSE) : REM (sprite number, ink, paper, bright, flash)
    fspAttr(1,YELLOW,BLACK,FALSE,FALSE)
    fspAttr(2,GREEN,BLUE,TRUE,FALSE)
    fspAttr(3,RED,WHITE,FALSE,FALSE)

    goto endofProgram    

mainLoop:

    IF MULTIKEYS(KEYO) and gx>0 THEN LET gx=gx-1: END IF
    IF MULTIKEYS(KEYP) and gx<30 THEN LET gx=gx+1:END IF
    IF MULTIKEYS(KEYQ) and gy>0 THEN LET gy=gy-1 :END IF
    IF MULTIKEYS(KEYA) and gy<22 THEN LET gy=gy+1: END IF


    let dx=dx+dmx:if dx=0 or dx=30 then let dmx=-dmx : END IF
    let dy=dy+dmy:if dy=0 or dy=22 then let dmy=-dmy: END IF
    
    let tx=tx+tmx:if tx=0 or tx=30 then let tmx=-tmx: END IF
    let ty=ty+tmy:if ty=0 or ty=22 then let tmy=-tmy: END IF
    
    let cx=cx+cmx:if cx=0 or cx=30 then let cmx=-cmx: END IF
    let cy=cy+cmy:if cy=0 or cy=22 then let cmy=-cmy: END IF

    fspCoord (0,gx,gy)
    fspCoord (1,dx,dy)
    fspCoord (2,tx,ty)
    fspCoord (3,cx,cy)

    fspRedraw()
    return

    endofProgram:
    
    
    fspErase()
    cls
    REM let's call a few things before we drop out to the IM2 mode so that the -O3 doesn't kill it
              REM Sadly, It does break it anyway!
    fspUpdate()
    gosub mainLoop
    im2Initialize()

O# Test: It does indeed compile without a hitch, and -O3 does indeed break it HARD!. At first it was because a lot of functions were not called; so I called them. Not quite sure what it's optimizing out that would break it now.

Anyway, that's how I did it; like I said sort of hammering it into place. But it definitely worked.
Reply
#8
britlion Wrote:
boriel Wrote::o Wooow!

Don't know whether you use ZX BASIC to compile that. If you did, I guess -O3 won't work with this, as it won't probably understand IM2 routines, etc...
Again, astonishing!!!

I was wondering when you'd notice that little gem of fun.
I was busy fixing -O3 up (I deeply rewrote some routines of the module). So I could only test this great program once I finished fixing it.

britlion Wrote:Yes, it's ALL done with ZX Basic and as for -O3, I actually didn't, because I've been avoiding use of O3 etc - or was at that time; you want us to test it now :-)

It shouldn't make a difference, I think, because I sorta used a hammer to make it work. I used the routine listed above - which pokes the IM2 system into place the hard way. I couldn't find a way to let it allow me to put code where I wanted any other way.
Ok, this is is something hard to do. I mean, if you use ORG XXXXX It will place the following code at that place, including the rest of the basic program.
Idea Idea: Why don't you put this in the wishlist (yes, it will take MUCH time, but not discarded)? For example:
  • Capability to declare functions or sub at a given memory address, like when declaring a variable with DIM .. AT @.... Example:
    Code:
    SUB MyInterrupt AT 0xFFE0h
         [...]
         END SUB
  • Capability to declare sub interrupts (this is already used in other compilers).
    Code:
    SUB INTERRUPT MyInterrupt : REM No parameters and no return. Must be a SUB
         [...]
         END SUB
    or more idiomatic
    Code:
    SUB Spritty AS INTERRUPT
         [...]
         END SUB
Some compilers allows to declare several interrupts and chain them, but I think one should suffice.
Britlion Wrote:Here's the actual whole program (and I know the IM2 bit isn't as efficient as it could be, it was a first pass, and hey, it worked):
I also thought it was. Again, thank you!

britlion Wrote:[... code ... ]

O# Test: It does indeed compile without a hitch, and -O3 does indeed break it HARD!. At first it was because a lot of functions were not called; so I called them. Not quite sure what it's optimizing out that would break it now.

Anyway, that's how I did it; like I said sort of hammering it into place. But it definitely worked.
Ok
Nice to hear that. Tongue Regarding to the routines being removed by -On, yes this is a problem I'm addressing. The compiler does not know anything about user's ASM block content (that's at the assembling stage). The reason is this is a "generic architecture" compiler (or a wanna be). So, for example, if I port the compiler to Commodore or PS3 (yes, it could be done, in fact), the assembler will be completely different. -O3 module has problem with this, because it's the nexus between BASIC layer (frontend) and asm/target machine layer (backend). In fact, such module must be completely rewritten for every architecture.

Having said that, I've thought some solutions:
  1. Put function calls at the end of the program, if you don't want them to be removed.
  2. The above does not prevent Variables to be removed, so if the ASM block write on basic variables,
    this could lead to another problem. So we can tell the compiler which identifiers (Variables, SUBs, Functions) our ASM block requires:
    Code:
    DIM MyVar1, MyVar2 as uInteger

        SUB MyFunction
        [...]
        END SUB

        ASM USES { MyFunction, MyVar1, MyVar2 }
        [...]
        call MyFunction
        ld (MyVar1), hl
        END ASM
    or alike
What do you think? If you like this, please, add it in the wishlist :wink:
Reply
#9
I love some of these ideas.

I think SUb <name> as interrupt has fascinating possibilities - not least of which that the compiler could assume this entry point will happen, and follow that thread to see what variables and functions are used, as well as main.

It probably means two passes, but it's a way of allowing -O3 to not exclude the interrupt path of the program.

Also, if it can work out where it has the memory to do the vector table and ISR (probably in 2 pages at the end of the program, if there's room), then that's awesome.

I hate to waste memory though. I think I'll wishlist a memory map option too.
Reply
#10
Shame this doesnt seem to work properly any more. Looks like an issue with fourspriter
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)