Jump to content


Photo

source code for JSW

members only source code jsw

  • Please log in to reply
66 replies to this topic

#21 jetsetdanny

jetsetdanny

    Advanced Member

  • Contributor
  • 2,133 posts

Posted 07 July 2019 - 02:05 AM

Great stuff, Norman Sword!  :thumbsup:



#22 IRF

IRF

    Advanced Member

  • Contributor
  • 4,275 posts

Posted 08 July 2019 - 03:20 PM

These labels (in the room expansion code) made me chuckle:

 


tile_it:

tile_grout:

 

 

******

 

Regarding the clock update, I came up with the use of XOR to toggle between 'am' and 'pm' independently, and suggested it to Metalmickey for his 'As Manufacturer Intended' project.  But the rest of the method is an innovation - ditching the use of an indexed register (IX) saves quite a few bytes (no need to use the 'shift opcode' DD), and the comparison against a table of 'upper limit values' for each digit ("clock_master") is rather ingenious!


Edited by IRF, 08 July 2019 - 09:47 PM.


#23 IRF

IRF

    Advanced Member

  • Contributor
  • 4,275 posts

Posted 09 July 2019 - 11:33 AM

I think I might(?) have come up with a further optimisation of the room expansion code - I haven't tried it out yet though...

 

Suggested changes are in red:

 

tile_grout:
    LD IX,att_master ; 8D36 ;$5E00  Point IX at the first byte of the attribute buffer at 5E00
    ld de,char_master
tile_do:
    ld a,(ix)
    ld c,a
    add a,a
    add a,a
    add a,a
    add a,c                      ;*9
    add a,low back_tile
    ld l,a
    ld h,high back_tile
    ld a,(hl)  ;tile colour
    ld (ix),a
    inc ix                      ;Crosses page boundary at mid point
    inc l
    ld c,d
    call print_non-ASCII_char     ;copies data from (hl) to (de):- SCREEN MAPPED  see source code for address ; Label renamed for clarity
    ld a,d
    ld d,c

    inc e
    jr nz,tile_do              ;at this point we cross the page boundary 
    LD A, D

    ADD A, #08

    ld d,a

    BIT 7, D                  ; Have we finished drawing the room (i.e. is DE now pointing at #8000)?
    cp high +(char_master+$1000)

    jr nz JR Z,tile_do       ; Conditionality of jump is reversed
    ret

 

 

 

EDIT: Now that I've tallied up the 'before versus after' difference, it might not be any more efficient in terms of memory after all!  There are fewer commands overall though, so perhaps it's marginally faster?

 

 

In addition to the above, the 'Print a message' (#9680) and 'Print a single character' (#9691) routines would need to be rewritten, as follows:

 

ORG #9680

LD A, (IX+$00)                         ; Collect a character from the message

CALL Print_ASCII_character   ; Print it

INC IX                                      ; Point IX at the next character in the message

INC E                                       ; Point DE at the next character cell

LD A,D                                     ; (subtracting 8 from D compensates for the operations performed by the subroutine at 9691

SUB $08

LD D,A

DEC C                                      ; Have we printed the entire message yet?

JR NZ,$9680                            ; If not, jump back to print the next character

RET

 

Print_ASCII_character:            ; Formerly #9691

LD H, #07

LD L, A

SET 7, L

ADD HL, HL

ADD HL, HL

ADD HL, HL

 

Print_non_ASCII-character:     ; Formerly #9699   ; Label renamed for clarity

LD B, #08                                 ; There are eight pixel rows in a character bitmap

Loop:                                         ; Copy the character bitmap (or triangle UDG, or item graphic) to the screen (or screen buffer)

LD A, (HL)

LD (DE), A

INC HL

INC D

DJNZ Loop

LD A,D                                    ; Subtracting 8 from D compensates for the operations performed by this subroutine

SUB $08

LD D,A

RET

 

 

As I say, I haven't tested this out yet.  Off the top of my head, I can't think of any reason why it wouldn't work, but careful consideration is needed bearing in mind that the 'Print a (non-ASCII) character' late entry point into the 'Print a single character' subroutine is also used by other code (such as the Impossible Triangle and item-drawing routines).


Edited by IRF, 09 July 2019 - 11:37 AM.


#24 IRF

IRF

    Advanced Member

  • Contributor
  • 4,275 posts

Posted 09 July 2019 - 12:15 PM


EDIT: Now that I've tallied up the 'before versus after' difference, it might not be any more efficient in terms of memory after all!

 

However, a byte could possibly be saved by replacing this (commands in green):

 

 


LD A, D

ADD A, #08

LD D, A

BIT 7, D                  ; Have we finished drawing the room (i.e. is DE now pointing at #8000)?
JR Z,tile_do       ; Conditionality of jump is reversed

RET

 

 

with this:

 

LD A, D

ADD A, #08

LD D, A

JP PO, tile_do    or    JP PE, tile_do                ; Overflow Flag is set when ADD 8 takes D from #78 to #80, but not when D goes from #70 to #78

RET

 

 

Again, I've not tried this out yet.  And I'm not currently sure if, when the Overflow Flag is set, whether that translates as Parity Odd or as Parity Even?  (But if one doesn't work - presumably causing the routine to bail out after drawing the pixels for the top half of the room only - then the opposite conditionality for the jump should do the trick.)

 

 

EDIT: In this instance, testing the Sign Flag (i.e. JP P or JP M) would have the same effect as testing Overflow.

 

In either case, there is only an absolute jump available for this purpose, not a relative jump.  So only one byte can be saved, not two.


Edited by IRF, 09 July 2019 - 12:35 PM.


#25 Norman Sword

Norman Sword

    Advanced Member

  • Member
  • PipPipPip
  • 237 posts

Posted 09 July 2019 - 03:19 PM

Rope compression:

 

 

code originally sits from $92a4 up to $93d1 

 

TOATAL saving from code around $102    258 bytes

 

;----------------------------------------------------------------------------------------------------
; the original rope had one entry per byte      00000xxxb
; the compact had two entries per byte  11112222b    ; where 1111=y and 2222=x and are data entries
; the super compact                             11223344b ;where 11=y 22=x  (odd)   33=y, 44=x (evens)

;        SUPER COMPACT ROPE

;-------------------------------------------------------------------------------------------------

;only 45 (at time of looking) smaller

;this mass of code and code change might seem pointless to save around 45 bytes.
;Its true purpose is to reduce the rope table from 256 bytes down to around 43 bytes. saving 213 bytes on the rope table

      org $92a4

;We are dealing with a rope.
draw_rope:
    LD IY,ytable  ;L92A4 ;L8200  set iy at the start of the y table
    LD (IX+rope09),$00 ; 92A8 ; Initialise the segment count
    LD A,(IX+rope02) ; 92AC ; copy the initial x position
    LD (IX+rope03),A ; 92AF ;      into the current x position
    LD (IX+rope05),$80 ; 92B2 ; define the pixel to draw; start at bit 7

; if hl waa passed as hl=ix
;
; inc hl ;+1
; inc hl ;+2
; ld a,(hl)
; inc hl ;+3
; ld (hl),a   ; 5 bytes compared to the 12 used

 

;The following loop draws each segment of the rope from top to bottom.
rope_segments:
    LD A,(IY+$00)      ;L92B6 ;L92B6 extract the ls from the current y table position
    ld b,(ix+rope03)                ;  X OFFSET
    add a,b   ; ; add the x offset
    LD L,A               ; 92BC ; the ls formed
    LD H,(IY+$01)    ; 92BD ;       get the ms from the y table ;hl=address to consider
;Hl=address to draw pixel
; set some variables up
   LD d,(IX+rope05)        ; ; drawing byte (pixel)
   ld e,(ix+rope09) ; ; current segment count
; are we hooked onto the rope
    LD A,(rope_status) ; 92C0 ;L85D6 

    OR A   ; 92C3 ; Is Willy on the rope, or has he recently jumped or dropped off it?
    JR NZ,L92D6  ; 92C4 ; Jump if so
; not hooked but is the about to be drawn pixel occupied
    ld a,d                  ; ;     Pick up the drawing byte
    AND (HL)  ; 92C9 ; Is this segment of rope touching anything else that's been drawn so far (e.g. Willy)?
    JR Z,L930E  ; 92CA ; Jump if not
; indicate we hit something here, and hook willy on, at this position
    ld a,e                  ;       ;     Copy the current segment counter into the rope status indicator at L85D6
    LD (rope_status),A ; 92CF ;L85d6
    SET ropebit,(IX+rope0B) ; 92D2 ; ropebit=0, Signal: Willy is on the rope
; again is willy hooked and do we need to adjust willies position and phase
L92D6:
    cp e   ; ; Does the rope status indicator at L85D6 match the current segment counter?
    JR NZ,L930E  ; 92D9 ; Jump if not

    BIT ropebit,(IX+rope0B) ; 92DB ; ropebit=0, Is Willy on the rope (and hooked to this segment)?
    JR Z,L930E  ; 92DF ; Jump if not
    ld a,d    ; ; Pick up the drawing byte(pixel) in A

; we adjust the position of willy depending on the pixel he is hooked onto
;pixel      phase  position adjust
;10000000\  c=2    b=-1
;01000000/

;00100000\  c=3    b=-1
;00010000/

;00001000\  c=0
;00000100/

;00000010\  c=1
;00000001/

    LD C,$01  ; 92E7 ; C=0=Willy's next animation frame
    CP $04    ; 92E9 ; bit 0 or 1
    JR C,L92FC  ; 92EB ;     Jump if so
    dec c   ; 92ED ; c=1=Willy's next animation phase
    CP $10    ; 92EF ; bit 2 or 3?
    JR C,L92FC  ; 92F1 ;  Jump if so
    DEC B   ; 92F3 ; Decrement the x-coordinate
    LD C,$03  ; 92F4 ; c=3=willy's next animation phase
    CP $40    ; 92F6 ; bit 4 or 5?
    JR C,L92FC  ; 92F8 ;  Jump if so
    dec c   ; 92FA ; c=2=Willy's next animation frame
    L92FC
    LD (willy_anim),BC ;L92FC ;L85D2  Set Willy's animation frame at L85D2, and temporarily store his x-coordinate at L85D3
    LD A,IYl  ; 9300 ; Update Willy's pixel y-coordinate location as the rope moves
    SUB $10   ; 9302 ;
    PUSH HL   ; 9307 ; Save HL briefly

; uses willy y and x to generate its att position in hl (willy_att -which is updated)
; the call only destroys hl and af
; note call is to the opcode before where it used to call

    CALL update_willy_att_a ; ;  Update Willy's attribute address as Willy and the rope moves
    POP HL    ; 930B ; Restore the screen buffer address of the segment of rope under consideration to HL

L930E:

;draw pixel to buffer
      ld a,d                  ;       ;rope05  Draw a pixel of the rope
    OR (HL)   ; 9311 ;
    LD (HL),A  ; 9312 ;
; now move the pointers, CALCULATE THE NEXT PIXEL TO DRAW and where
;IY IS MOVED DOWN THE table by the offset specified in the rope table
    ld a,e       ; ; rope09 current segment
    LD c,(IX+rope01) ; ; start offset and direction
    add A,c   ; ;
    LD H,high rope_table ; 931C ;$83
;a is the total offset but I have compressed the data into half its original offset data space. (data is 1/4 original size)
and 01111111b  ;  ;  force positive - REMOVE THE side bit
    sra a   ; ;  half and find what bit 0 was (odd or even)
    ld l,a   ; ;  l is compensated offset into data
    ld a,(hl)  ; ;  the carry bit dictates iF the data is held in the high or low nibble
; hl not used from here; "L" will be re assigned
    jr c,nib_low  ; ;  go depending on odd or even extract
    rrca
    rrca
    rrca
    rrca
nib_low:

    and 15   ; ;   the four bits hold the y offset in 1100b and the x offset in 0011b
    ld l,a   ; ;   temp save
    and 1100b        ; ;   this is times 4 y value, but y value is a word offset so still needs /2
    rrca
; move the y position
    add a,iyl   ; ;  adjust the y offset
    ld iyl,a  ; ;  save new offset
; now consider the x
    ld a,l   ; ;  how much does the pixel move in the x direction
    and 3
    ld b,a      ;  ;  this is the rotate count
    jr z,b_was_zero  ; ;  if no displacement then don't bother calculating new x offset
    ld a,d   ;  ;  the pixel value from (ix+rope5)
    ld l,(ix+rope03)   ;  ;  X POS
;b=rotate c=rope01=sign  l=rope03=x  a=d=rope05=bit  e=rope09

; rotate the pixel either left or right and adjust the x-position when pixel has moved to next byte

    bit 7,c     ; ;  rope01
    jr z,go_left
go_right:
    rrca   
    jr nc,go_r1
    inc l
go_r1:
   djnz go_right
    jr gone

go_left:
    rlca
    jr nc,go_l1 
    dec l
go_l1:
    djnz go_left
gone:
    ld (ix+rope03),l ;  ;  the new x offset
    ld (ix+rope05),a ;  ;  the new byte(pixel)
;come here if no shift in the pixel position
b_was_zero:
    ld a,(ix+rope04) ; ;  the length of the rope
    inc (ix+rope09)  ; ;        one segment more   (note rope09 not used after here, if we abort loop)
    cp e   ; ;  have we reached full length yet
    Jp nz,rope_segments ;  ;  Jump back to draw the next segment of rope

;Now that the entire rope has been drawn, deal with Willy's movement along it.
L935E
;has willy just jumped off the rope
    ld hl,rope_status ; ;
    ld a,(hl) 
    bit 7,a   ;  if on the rope this value is less than 128
     ;  if the value is over 128 then we are flagged as in free flight just after leaving the rope
    jr z,L936F   ;
    inc (hl)  ;  ; the period of free flight is counted down
    RES ropebit,(IX+rope0B) ; 9369 ; ropebit=0, Signal: Willy is not on the rope
    JR next_entity_draw ; 936D ;L93b3  Jump to consider the next entity

; is he clutching onto the rope
L936F:

    BIT ropebit,(IX+rope0B) ;L936F ; ropebit=0, Is Willy on the rope?
    JR Z,next_entity_draw ; 9373 ;L93b3  go If not to next entity
;on the rope so decide if rope slide up/down needed
    LD A,(willy_dir) ; 9375 ;L85D0  Pick up Willy's direction and movement flags from L85D0
    BIT 1,A   ; 9378 ; Is Willy moving up or down the rope?
    JR Z,next_entity_draw ; 937A ;L93b3  If not, jump to consider the next entity
;calculate the direction of slide
    RRCA   ; 937C ; XOR Willy's direction bit (0=facing right, 1=facing left) with the rope's direction bit
;;    ;; ; (0=swinging right to left, 1=swinging left to right)
    XOR (IX+rope00)  ; 937D ;
    RLCA   ; 9380 ;
    RLCA   ; 9381 ; Now A=1 if Willy is facing the same direction as the rope is swinging (he will move down the rope),
;;    ;; ; or -1 otherwise (he will move up the rope)
    AND $02   ; 9382 ;
    DEC A   ; 9384 ;

;delete this is already set up
;; LD HL,rope_status ; 9385 ;L85d6  Increment or decrement the rope status indicator at L85D6
    ADD A,(HL)  ; 9388 ;

    LD (HL),A  ; 9389 ;

    LD A,(room_up)   ; 938A ;L80EB  c=room above
    LD C,A    ; 938D ;
    LD A,(current_room) ; 938E ;L8420 
    CP C   ; 9391 ; Is the room above the same as the current room
    JR NZ,L939B  ; 9392 ;  Jump if so
;limit movement up the rope
    LD A,(HL)  ; 9394 ; rope status
    CP $0C    ; 9395 ; check for the minimum value we can move up the rope
    JR NC,L939B  ; 9397 ; Jump if so
    LD (HL),$0C  ; 9399 ; moved to far so set to the minimum
; check for limit moving down the rope
L939B:

    LD A,(HL)  ;L939B ; rope status indicator
    CP (IX+rope04)   ; 939C ; Compare it with the length of the rope
    JR C,next_entity_draw ; 939F ;L93b3  any adjustment that has taken willy off the bottom of the rope detach willy
    JR Z,next_entity_draw ; 93A1 ;L93b3  let him cling to the last pixel
; detach willy from the rope, he has slid off the end
    LD (HL),$F0  ; 93A3 ; Set the rope status indicator at L85D6 to 0xF0 (Willy has just dropped off the bottom of the rope)
    ld hl,willy_y
    LD A,(hl)   ;  ;  Round down Willy's pixel y-coordinate at L85CF to the nearest multiple of 8;
    AND $F8   ; 93A8 ;
    LD (hl),A   ;  ;
    XOR A   ; 93AD ; Initialise the airborne status indicator at L85D1
    LD (airborne),A  ; 93AE ;L85D1

;

;The current entity has been dealt with. Time for the next one.
next_entity_draw:
    LD DE,$0008  ;L93B3 ; offset to next entity
    ADD IX,DE  ; 93B6 ; add offset so we point at the next entity
    JP L91C2  ; 93B8 ;

 

;+ the rope table which sits at $8300 reduced from 256 bytes

rope_table:

 DEFB (6*2+0)*16+6*2+0,(6*2+0)*16+6*2+0,(6*2+0)*16+6*2+0,(6*2+0)*16+6*2+0

 DEFB (6*2+0)*16+6*2+0,(6*2+0)*16+6*2+0,(6*2+0)*16+6*2+0,(6*2+0)*16+6*2+0
 DEFB (6*2+0)*16+6*2+0,(6*2+0)*16+6*2+0,(6*2+0)*16+6*2+0,(6*2+0)*16+6*2+0
 DEFB (6*2+0)*16+6*2+0,(6*2+0)*16+6*2+0,(6*2+0)*16+6*2+0,(6*2+0)*16+6*2+0
 DEFB (6*2+1)*16+6*2+1,(6*2+1)*16+6*2+1,(6*2+1)*16+6*2+1,(6*2+1)*16+6*2+1
 DEFB (6*2+1)*16+6*2+1,(6*2+1)*16+6*2+1,(6*2+2)*16+6*2+2,(6*2+2)*16+6*2+2
 DEFB (4*2+2)*16+6*2+2,(6*2+2)*16+4*2+2,(6*2+2)*16+4*2+2,(6*2+2)*16+4*2+2
 DEFB (6*2+2)*16+4*2+2,(4*2+2)*16+4*2+2,(6*2+2)*16+4*2+2,(4*2+2)*16+4*2+2
 DEFB (4*2+2)*16+4*2+2,(4*2+1)*16+4*2+2,(4*2+2)*16+4*2+1,(4*2+1)*16+4*2+2
 DEFB (4*2+1)*16+4*2+1,(4*2+2)*16+4*2+2,(4*2+3)*16+4*2+2,(4*2+3)*16+4*2+2
 DEFB (4*2+3)*16+4*2+3,(4*2+3)*16+4*2+3,(4*2+3)*16+4*2+3

 

 

see original listing for references

 

 

TOTAL SAVING around 258 bytes ($102)


Edited by Norman Sword, 09 July 2019 - 03:36 PM.


#26 Norman Sword

Norman Sword

    Advanced Member

  • Member
  • PipPipPip
  • 237 posts

Posted 09 July 2019 - 04:15 PM

                     Current saving                 Total saving 

Rope compression    258                          258
cheat code                51                           309 

room draw                 50                           359

lower att draw +

logo draw                400+                         759+

twinkle                     39                            798+

clock update            14                             812+

 



#27 Norman Sword

Norman Sword

    Advanced Member

  • Member
  • PipPipPip
  • 237 posts

Posted 09 July 2019 - 05:25 PM

ROOM JUMP ROUTINES 

 

sits from $948a to $94F9  this routine is 36 bytes smaller ; see source code for references

 

 

; ;948A: Move Willy into the room to the left
move_room_left   ;L948A ;
    LD BC,$E01E ;$FE1E ;
    LD HL,room_left  ; point at new direction

GO_NEW_ROOM:
    LD A,(HL)  ; get new room
    LD (current_room),A;L8420  ; set new room
    LD HL,willy_att
    LD A,(HL)
    AND B
    OR C
    LD (HL),A                ;L85D3 ; set new x pos
    POP HL                  ; Drop the return address (89D1, in the main loop) from the stack
    JP enter_new_room ;L8912 ; Draw the room and re-enter the main loop

 

move_room_right:       ;L949E  ;
    LD BC,$E000
    LD HL,room_right
    JR GO_NEW_ROOM

 

.
move_room_up:         ;L94B0 ;
    LD BC,$1FA0
    LD HL,room_up
; page reference
    ld de,$d001+(high (att_work))
    XOR A
VERT_PROCESS:
    LD (airborne),A
    LD A,E
    LD (willy_att+1),A
    LD A,D
    LD (willy_y),A
    JR GO_NEW_ROOM

 

move_room_down:        ;L94D2 ;
    LD BC,$1F00
    LD HL,room_down
;page reference
    ld de,$0000+(high (att_work))
    LD A,(airborne)      ;L85D1 ;
    CP $0B                ; Is it 0x0B or greater (meaning Willy has already been falling for a while)?
    JR NC,VERT_PROCESS  ; Go if so
    LD A,$02   ; Otherwise set the airborne status indicator to 2 (Willy
;     ; will start falling here if there's no floor beneath him)
    JR VERT_PROCESS

 

 

 

 


  • IRF likes this

#28 Norman Sword

Norman Sword

    Advanced Member

  • Member
  • PipPipPip
  • 237 posts

Posted 09 July 2019 - 05:27 PM

                       Current saving                 Total saving

Rope compression    258                          258
cheat code                51                           309
room draw                 50                           359
lower att draw +
logo draw                400+                         759+
twinkle                     39                            798+
clock update            14                             812+

room exits               36                             848


Edited by Norman Sword, 09 July 2019 - 05:27 PM.


#29 Norman Sword

Norman Sword

    Advanced Member

  • Member
  • PipPipPip
  • 237 posts

Posted 09 July 2019 - 06:10 PM

subtle change to the conveyor routine - saves 9 bytes 

 

just a logic flow change: no attempt at reducing code

 

 

;94F9: Move the conveyor in the current room
move_conveyor:
    LD HL,(conv_add) ;L94F9 
    LD A,H            ; 94FC 
    AND $01          ; 94FD
    RLCA              ; 94FF ;
    RLCA              ; 9500 ;
    RLCA              ; 9501 ;
   ADD A,high char_master'
    LD H,A        ; 9504 ;
    LD E,L        ; 9505 ;
    LD D,H       ; 9506 ;
    LD A,(conv_len)
    OR A         ; 950A 
    RET Z        ; 950B 
    LD B,A      ; 950C
    LD A,(conv_dir) 
    OR A               

;********************************* 

    JR  NZ,move_conv_right      Z,move_con_left         change condition and label

 

    ex de,hl                                                             add one opcode

;***********************************

 

;The conveyor is moving left.

move_conv_left

    LD A,(HL)  ; 9513 

    RLC A       ; 9514 ;
    RLC A   ; 9516 ;
    INC H   ; 9518 ;
    INC H   ; 9519 ;
    LD C,(HL)  ; 951A 
    RRC C   ; 951B ;
    RRC C   ; 951D ;
L951F:

    LD (DE),A      ;L951F 
    LD (HL),C      ; 9520
    INC L            ; 9521
    INC E           ; 9522 
    DJNZ L951F  ; 9523 
    RET             ; 9525 ;

 

 

; delete all the code below

 

;The conveyor is moving right.
move_conv_right:
    L9526 LD A,(HL)  ;L9526
    RRC A               ; 9527 
    RRC A               ; 9529 ;
    INC H                ; 952B 
    INC H                ; 952C ;
    LD C,(HL)          ; 952D
    RLC C              ; 952E 
    RLC C              ; 9530 ;
   JR L951F           ; 9532 ;

 

a subtle nine bytes shorter by altering one opcode and adding ex de,hl

 



#30 Norman Sword

Norman Sword

    Advanced Member

  • Member
  • PipPipPip
  • 237 posts

Posted 10 July 2019 - 02:28 PM

We can delete the room storage buffer. This is not used in the game and takes up 128 bytes.

 

( slightly updated:- since this is only referenced twice)

 

 

We add a new label  called 

 

S_M_C_pointer_room_data    which I will come to later 

 

then change part of the routine enter_new_room: at $891a from

 

    LD DE,room_layout ; 891A ;L8000 Copy the room definition into the game status buffer at L8000
    LD BC,$0100          ; 891D ;

    LDIR     

 

To

 

  ld (S_M_C_pointer_room_data),hl    ; save where this data is - needed for the room expansion routine
  ld bc,$80
  add hl,bc
  ld de,room_name

  LDIR

 

Then change the start of the routine draw_room   at  $8d33

from

 

    LD HL,room_layout   

 

to 

 

S_M_C_pointer_room_data equ $+1

    ld hl,$-$

 

 

See source listing for references

 

This saves 128 bytes but uses an extra 4 bytes to set up the data

 

Call it a saving of 120 bytes


Edited by Norman Sword, 10 July 2019 - 11:05 PM.






Also tagged with one or more of these keywords: members only, source code, jsw

0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users