Megalextoria
Retro computing and gaming, sci-fi books, tv and movies and other geeky stuff.

Home » Digital Archaeology » Computer Arcana » Apple » Apple II » Ramworks, Checkmate et al.
Show: Today's Messages :: Show Polls :: Message Navigator
E-mail to friend 
Switch to threaded view of this topic Create a new topic Submit Reply
Ramworks, Checkmate et al. [message #242034] Mon, 03 February 2014 10:59 Go to next message
mdj is currently offline  mdj
Messages: 301
Registered: December 2012
Karma: 0
Senior Member
Howdy,

As some of you would be aware I recently designed a clone of the Applied Engineering RamWorks card to show off at OzKest. I've been offered some assistance in 'productionising' my prototype which at present is a schematic on a bit of scrap paper and the functional prototype on the breadboard. Those who haven't seen it can check out Andrew Roughan's excellent photographs here:

http://www.flickr.com/photos/10917598@N06/9406879499/in/phot ostream/

I'm in the process of testing the design at lower voltages so that I can use larger capacity SRAMs (The pictured prototype uses a 16mbit SRAM configured as 2MBx8). That's a necessary design change for production cost reasons but there is one more potential design consideration of note I should point out.

Clones of the RamWorks cards respond to a bank register change on a write to any address in $C07X, the range that fires the paddle strobe. If you look at the signal availability on the Auxiliary slot it becomes apparent why this is; it's the only way to do it. The RamWorks however has gone to particular trouble to respond only to a write on address $C073. I had originally overlooked this for the purposes of getting the design working with the intention to revisit, but having given it some thought I'm not entirely sure which way I should go.

The only purpose AE's design tweak seems to serve (in addition to being less susceptible to a phantom bank change) is actually in providing a measure of copy protection for their software. The RAM test fails on a non-AE card (mind included) and their provided Ramdisk software also fails to run. This is done by producing a phantom write to another $C07X address that on other cards changes the current bank but on the RamWorks card does nothing. Of course, programs like AppleWorks recognise the memory just fine.

So, implementing the RamWorks $C073-only feature seems to only have utility in gaining compatibility with existing RamWorks software, principally the RamDisk emulators, and perhaps some measure of compatibility with other peripherals that may also borrow IO space in $C07X that I haven't heard of.

For once in my life I don't really have an opinion on the 'right way' to go.. In a way, my card is already not 100% compatible since it maps banks to register numbers linearly (RamWorks only does this up to 1MB) so the RAM test software for RamWorks is somewhat useless, and the Checkmate technology cards implement bank switching the same way I have and apparently worked just fine.

Does anyone have an opinion on this ? I'm inclined to implement the strict AE way just for the mental exercise but it does add a chip or two to the otherwise lean design. I'd feel a lot better about not doing so if the Checkmate software was 'out there' on the archives. Does anyone have that, or know of any other RamWorks-style RAM drive software I may have missed?

TIA

Matt
Re: Ramworks, Checkmate et al. [message #242059 is a reply to message #242034] Mon, 03 February 2014 14:05 Go to previous messageGo to next message
STYNX is currently offline  STYNX
Messages: 453
Registered: October 2012
Karma: 0
Senior Member
On Monday, February 3, 2014 4:59:17 PM UTC+1, mdj wrote:
> Howdy,
> As some of you would be aware I recently designed a clone of the Applied Engineering RamWorks card to show off at OzKest. ...
> ...

Interesting, to say rye least :-)

You might be interested in asking this Guy http://www.ebay.de/itm/160968650489 for chips.
He offered me $1.50/pc at 40pcs minimum volume per order. I haven't seen them cheaper anywhere else. They are only 8mbit (1mx8) 55ns though and therefore too slow for my projects.

-Jonas
Re: Ramworks, Checkmate et al. [message #242060 is a reply to message #242059] Mon, 03 February 2014 14:06 Go to previous messageGo to next message
STYNX is currently offline  STYNX
Messages: 453
Registered: October 2012
Karma: 0
Senior Member
On Monday, February 3, 2014 8:05:27 PM UTC+1, STYNX wrote:
> Interesting, to say rye least :-)
Interesting, to say THE least :-)
.... what the hell happened there :-P
Re: Ramworks, Checkmate et al. [message #242061 is a reply to message #242034] Mon, 03 February 2014 14:19 Go to previous messageGo to next message
STYNX is currently offline  STYNX
Messages: 453
Registered: October 2012
Karma: 0
Senior Member
On Monday, February 3, 2014 4:59:17 PM UTC+1, mdj wrote:
> Howdy,
> ...
> For once in my life I don't really have an opinion on the 'right way' to go.
> ...
> Does anyone have an opinion on this ?
> ...
> Matt

Hi Matt,

after i posted the hint to the ebay guy, i reread your post. I think that there is no 'right way'. Personally i like the linear addressing more as it seems a little more 'neat'. A nice (and cheap) 2mb (or bigger) memory expansion might be cool, especially if it can be buffered with a button cell for several months or even years. A fun project indeed ;-)

-Jonas
Re: Ramworks, Checkmate et al. [message #242077 is a reply to message #242034] Mon, 03 February 2014 15:28 Go to previous messageGo to next message
gids.rs is currently offline  gids.rs
Messages: 1395
Registered: October 2012
Karma: 0
Senior Member
Does anyone have that, or know of any other RamWorks-style RAM drive software I may have missed?


> Matt


part of Glen Brendon's ProSEL



LST OFF
ORG $2000
TYP $FF

********************************
* *
* RAM.DRV.SYSTEM (type SYS) *
* Driver ProDOS carte Ram *
* RamWorks & Checkmate Tech *
* (c) Glen Bredon (Prosel-8) *
* Source by Deckard *
* http://boutillon.free.fr *
* *
********************************

* Assembleur: Merlin-8 2.58 (ProDOS)
* Last update: 06 jul 2006

********************************

* Commandes MLI

QUIT = $65
GET_FILE_INFO = $C4
ONLINE = $C5
OPEN = $C8
READ = $CA
CLOSE = $CC
GET_EOF = $D1

* Page 0 main memory
* Parameters device driver

DRV_COMMAND EQU $42 ; command
DRV_BUFFL EQU $44 ; buffer low main mem pour read/write
DRV_BUFFH EQU $45 ; buffer high main mem pour read/write
DRV_BLKL EQU $46 ; block low @ lire ou {crire
DRV_BLKH EQU $47 ; block high @ lire ou {crire

* Ram princ

ONLINE_BUF EQU $0220 ; $0220-$022F Buffer MLI Online
BANK_OK EQU $248C ; $18 octets ($248C-$24A3) Liste des banks retenus
; pour le disk virtuel de ram. Cette liste est mov{e
; dans l'espace $FFBE-$FFD5 du driver ramdisk. Les
; num{ros des banks ne sont pas forc{ment
; chronologiques.
WRK_NB_LCK_BK EQU $24B9 ; zone de travail nombre de banks bloqu{s
LAST_BLK_LCK EQU $24BA ; borne dernier bank bloqu{
BANK_LST EQU $24BB ; $80 octets stockage des banks ($FF=bad bank)
; No locked banks in this buffer (excluded)
BANK_LST_ALL EQU $253B ; $80 octets stockage des banks ($FF=bad bank)
; all banks in this buffer (incl. locked banks)
SAV_BYTE_00 EQU $25BB ; sauvegarde des octets $00 des $80 banks
SAV_BYTE_01 EQU $263B ; sauvegarde des octets $01 des $80 banks

* Ram aux (1er bank aux valide diff{rent de bank $00)

RAM_BLK0002L EQU $0600 ; block 2 : volume dir
RAM_BLK0002H EQU $0700
RAM_BLK0003L EQU $0800 ; block 3 : volume dir
RAM_BLK0003H EQU $0900
RAM_BLK0004L EQU $0A00 ; block 4 : volume dir
RAM_BLK0004H EQU $0B00
RAM_BLK0005L EQU $0C00 ; block 5 : volume dir
RAM_BLK0005H EQU $0D00
RAM_BLK0006L EQU $0E00 ; block 6 : volume bitmap
RAM_BLK0006H EQU $0F00
RAM_BLK0007L EQU $1000 ; block 7 : adresse d{but des datas

* Adresses ProDOS

MLI EQU $BF00 ; appel MLI ProDOS
DEVADR01 EQU $BF10 ; d{but des devices addresses (slot 0, drive 1)
DEVADR32 EQU $BF26 ; addr device driver slot 3, drive 2 (/RAM)
DEVCNT EQU $BF31 ; Count (minus 1) of active devices
DEVLST EQU $BF32 ; List if active devices (Slot, drive, id =DSSSIIII)
DATE EQU $BF90 ; date/time sur 4 octets

* Softswitchs

_80STORE0 EQU $C000 ; utilise main/aux read/wrt
_80STORE1 EQU $C001 ; acc}s page affich
MAINREAD EQU $C002 ; lecture en m{moire principale
AUXREAD EQU $C003 ; lecture en ram aux
MAINWRT EQU $C004 ; {criture en main memory
AUXWRT EQU $C005 ; {criture en ram aux
MAINZP EQU $C008 ; page 0 main memory (ALTZP) + RAMDISK_DRV on
AUXZP EQU $C009 ; page 0 ram aux (ALTZP) + RAMDISK_DRV off
RZPSW EQU $C016 ; lecture {tat ALTZP - read zero page switch
R80ST EQU $C018 ; read 80STORE
BUTTN1 EQU $C062 ; touche pomme-ferm{e=closed-apple (option)
BANKSEL EQU $C073 ; bank switching selector (ramworks et compatibles)
; ou aussi appel{ bank select register.

********************************

H2000 JMP H20E1 ; jump start

*-------------------------------
* Ram driver parameters
*-------------------------------

HEX EEEE ; header id for launcher (used by Block.Warden)
DFB 64 ; length of pathname buffer

; you have to change the following label to use it
; with your own disk!
NEXT_SYS STR '/ABOUT.RAM/BASIC.SYSTEM'
DS 40,0

HEX FFFF ; borne butoire pathname buffer
; not used (because len=64 here)

NB_LOCKED_BK DFB 0 ; nbr de banks bloqu{s (pr{servation usage user)
; lock out how many 64k banks of ramcard memory
; YOU CAN CHANGE IT!!

MAP_SLOT_DRV DFB %00000011 ; (drive*8)-8 + slot -> drive 1, slot 3 (default)
; mapping slot and drive 0000DSSS
; D : 0=drive 1, 1=drive 2 / SSS=slot number
; YOU CAN CHANGE IT!!

********************************
* *
* Prise en compte du prochain *
* .SYS mentionn{ en dur *
* *
********************************

* Run next SYS program

HBD00

ORG $BD00

LDX NEXT_SYS ; longueur next SYSTEM
BEQ :2 ; pas de nom mentionn{

:1 LDA NEXT_SYS,X ; recopie du nom en page 2 (buffer clavier)
STA $0280,X
DEX
BPL :1

JSR MLI , r{cup}re les infos de cet objet
DFB GET_FILE_INFO
DA TBL_GET_F_I
BCS :2 ; err->quit

LDX FILE_TYPE ; $FF (=system)+1
INX
BNE :2 ; pas un type SYS

JSR MLI ; ouverture du SYS
DFB OPEN
DA TBL_OPEN
BCS :3 ; err->quit

LDA OPEN_NUM ; dispatch le num{ro du fichier
STA READ_NUM
STA GET_EOF_NUM

JSR MLI ; recherche fin du fichier SYS
DFB GET_EOF
DA TBL_GET_EOF
BCS :3

LDA EOF_POS+2 ; 3 octets de taille?
BNE :3 ; si oui, trop gros ->err

LDA EOF_POS+1 ; check taille high
CMP #>$9800 ; $2000+9800
BCS :3 ; encore trop long

STA READ_REQ_LEN+1 ; parm read
LDA EOF_POS
STA READ_REQ_LEN

JSR MLI ; charge le SYS en $2000
DFB READ
DA TBL_READ
BCS :3 ; err->quit

JSR MLI
DFB CLOSE
DA TBL_CLOSE
BCS :3 ; err->re-try close+quit

JMP $2000 ; lance du .SYSTEM

:3 JSR MLI ; passe la main
DFB CLOSE
DA TBL_CLOSE

:2 JSR MLI ; passe la main
DFB QUIT
DA TBL_QUIT

*-------------------------------

* Parameters Quit

TBL_QUIT DFB 4
DS 6,0

* Parameters Get File Info

TBL_GET_F_I DFB $0A
DA $0280
DFB 0
FILE_TYPE DFB 0 ; file type
DS 13,0

* Parameters Open

TBL_OPEN DFB $03
DA $0280
DA $1C00
OPEN_NUM DFB 0

* Parameters Close

TBL_CLOSE DFB 1
CLOSE_NUM DFB 0

* Parameters Read

TBL_READ DFB 4
READ_NUM DFB 0
DA $2000 ; adresse implantation
READ_REQ_LEN DS 2,0
DS 2,0

* Parameters Get Eof

TBL_GET_EOF DFB 2
GET_EOF_NUM DFB 0
EOF_POS DS 3,0

********************************
* *
* Point d'entr{e de RAM.DRV *
* *
********************************

ORG
; $20E1
*-------------------------------
* Reloge le code de chargement
* du prochain SYS en $BD00
*-------------------------------

H20E1 LDY #0
:1 LDA HBD00,Y
STA $BD00,Y
INY
BNE :1

*-------------------------------
* Get ram size
*-------------------------------

H20EC STA _80STORE0 ; utilise ramread/ramwrite
LDY #0
STY BANKSEL ; bank 0
STA AUXZP ; utilise page 0 aux

LDA #$FF ; init les 2 buffers de $80 octets avec les listes
H20F9 STA BANK_LST,Y ; des banks ($FF=bad bank) locked et all.
INY ; 2*$80 = $100
BNE H20F9
; Y=0
* Backup adresses $00/$01 de chaque bank
* car ces adresses sont {cras{es pour le test

; start: bank 0 aux ($00/$01 page 0 princ not saved)
H20FF STY BANKSEL ; enclenche bank Y
LDA $00 ; backup $00 addr in buffer
STA SAV_BYTE_00,Y
LDA $01 ; backup $01 addr in buffer
STA SAV_BYTE_01,Y
INY ; for $80 banks ($00-$7F)
BPL H20FF

* Pour tester si un bank est valide, on l'enclenche
* en {crivant son num{ro @ l'adresse sp{ciale $c073
* puis on {crit dedans @ un endroit particulier. Si la
* relecture au meme endroit donne le meme r{sultat,
* cela veut dire que le bank est ok. Ici le test est
* r{alis{ en page 0 sur les octets $00 et $01.

* Phase d'{criture

DEY ; Y=$7F
H2110 STY BANKSEL
STY $00 ; {crit le num{ro du bank dans $00 (page 0 aux)
TYA
EOR #$FF
STA $01 ; {crit le checksum (=num bank eor #$FF) dans $01
DEY ; pour $80-1 banks
BNE H2110 ; ne fait pas le bank 0 aux ici

; Y=0 : fait le bank 0 ici car il faut faire
; aux et princ

STY BANKSEL ; fait le bank 0 aux
STY $00
STY $01 ; no eor
STA MAINZP ; puis la page 0 princ
STY $00
STY $01 ; no eor

STA AUXZP ; repasse en page 0 aux

LDA NB_LOCKED_BK ; place le nb de banks bloqu{s dans la zone
STA WRK_NB_LCK_BK ; de travail qu'on d{cr{mentera @ fur et @ mesure

* Phase de lecture et controle r{sultat

LDY #1 ; skip bank 0 aux (pr{serv{)
H2136 STY BANKSEL ; enclenche bank Y
CPY $00 ; compare avec valeur stock{e pr{cedemment
BNE H2166 ; pas pareil -> bad bank

TYA
EOR #$FF
EOR $01 ; compare checksum avec valeur sauv{e
BNE H2166 ; pas pareil -> bad bank

CPY $00 ; check $00/$01
BNE H2166 ; toujours pas bon -> {carte ce bank aux

TYA ; signale que le bank est valide dans la liste ALL
STA BANK_LST_ALL,Y ; stockage num{ro

LDX WRK_NB_LCK_BK ; reste-il des banks bloqu{s?
BNE H2154 ; oui

STA LAST_BLK_LCK ; non -> set borne dernier bank bloqu{

H2154 DEC WRK_NB_LCK_BK ; un bank bloqu{ en moins
BPL H2166 ; il y en a encore

STA BANK_LST,Y ; sauve le bank dans la liste restreinte

* Recopie @ partir de $00B0 la routine RWRDB
* dans ce bank aux valide

LDX #$45
H215E LDA H2355-1,X
STA $B0-1,X
DEX
BNE H215E

H2166 INY ; passe au bank suivant
BPL H2136 ; fait banks aux $01 @ $7F

* Restauration des donn{es alt{r{es dans chaque bank
* aux valide aux addr $00 et $01

; Y=$80
H2169 LDA BANK_LST_ALL-1,Y ; bank valide (<$80)?
BMI H2180 ; non: bad bank ($FF)

CMP LAST_BLK_LCK ; est-ce la borne bank bloqu{?
BEQ H2180 ; oui, ne fait pas les restaurations

STA BANKSEL ; enclenche bank acc
LDA SAV_BYTE_00-1,Y ; restore addr $00 previously saved
STA $00
LDA SAV_BYTE_01-1,Y ; idem adr $01
STA $01

H2180 DEY ; bank pr{c{dant
BNE H2169 ; ne fait pas le bank 0 aux
; Y=0
STY BANKSEL ; enclenche bank 0
STY $00 ; marque le bank 0 aux

* R{cup}re la liste finale des banks valides
* pour avoir les num{ros regroup{s. On se restreint
* @ la liste des banks aux valides {ligibles.

LDX #$FF ; X -> $FF+1=0
H218A INX ; indice sur BANK_OK
CPX #$18 ; dernier bank accept{?
BCS H219C ; oui, on arrete l@ (>=)

; skip bank 0 aux
H218F INY ; bank suivant
BMI H219C ; fin car on vient de parcourir $80 banks

LDA BANK_LST,Y ; bank valide?
BMI H218F ; non ($FF)

STA BANK_OK,X ; ajoute @ la liste finale
BPL H218A ; toujours

* Calcul du nbr de blocks
* Nbr de bank * 128 (- 8 blocks tous les 8 banks en
* comman\ant d}s le 1er bank).
* Si 01 bank -> 1*128 - 8 blocks = 120 blocks total
* Si 02 banks -> 2*128 - 8 blocks = 248
* etc...
* Si 08 banks -> 8*128 - 8 blocks = 1016
* Si 09 banks -> 9*128 - 16 blocks = 1136
* etc...
* Si 24 banks -> 24*128 - 24 blocks = 3048
*
* Les blocks r{serv{s (par lot de 8) correspondent aux pages
* 0 et 1 (pile=stack) de chaque bank.

; X=nbr de banks dans la liste
H219C TXA ; nbr de blocks * 128
LSR
STA VOL_TOT_BLK+1 ; high
ROR VOL_TOT_BLK
STX HFF55+1 ; sauve nbr de banks valides
DEX
STX H239E ; sauve nbr de banks-1
BMI GO_QUIT ; aucun bank valide r{servable pour le ramdisk

LDA VOL_TOT_BLK ; -nb de bank
SEC
SBC HFF55+1
AND #%11111000
STA VOL_TOT_BLK ; low
STA RWB_ST_BLKL+1
BCS H21C1

DEC VOL_TOT_BLK+1 ; high

H21C1 LDA VOL_TOT_BLK+1
STA RWB_ST_BLKH+1

LDA BANK_OK ; positionne sur le 1er bank aux valide qui est
STA BANKSEL ; diff{rent du bank 0 aux.
LDA $00 ; check si un moins un bank. Bank 0 aux interdit.
BEQ GO_QUIT ; aucun bank! On s'arrete ici.


*-------------------------------
* Pr{paration volume /RAM
*-------------------------------

* Laisse une trace dans le 1er bank aux valide pour identifier
* le driver en place (signature avec nom de l'auteur!)

LDX #2 ; check signature bank aux
H21D3 LDA H23A0,X ; trigramme nom de l'auteur
CMP $06,X ; existe d{ja?
BNE H21EB ; pas encore sign{. Pr{paration du volume @ faire.

DEX ; caract}re suivant
BPL H21D3
; la signature est pr{sente. Donc normalement
; ce volume a d{ja {t{ formatt{ logiquement.
; N{anmoins, l'utilisateur peut demander @ forcer
; un reformattage. Teste la touche sp{ciale.
; X=$FF
BIT BUTTN1 ; test touche pomme-ferm{e=closed-apple (option)
BMI RAM_FORMAT ; oui, touche enfonc{e -> formattage demand{

JMP H2276 ; laisse en {tat et passe @ la suite.

GO_QUIT JMP RAMDISK_QUIT

H21E8 LDA H23A0,X ; set signature
H21EB STA $06,X ; page 0 1er bank aux valide de la liste
DEX
BPL H21E8

* Formattage du ram disk

RAM_FORMAT STA MAINZP
LDY #3 ; date/time volume
H21F5 LDA DATE,Y ; donn{e par la prodos system global page
STA VOL_DATETIME,Y
DEY
BPL H21F5 ; >=0

STA AUXWRT ; {criture bank aux
INY ; Y=0
TYA ; acc=0. Ecrase blocks 2 @ 6.
H2203 STA RAM_BLK0002L,Y
STA RAM_BLK0002H,Y
STA RAM_BLK0003L,Y
STA RAM_BLK0003H,Y
STA RAM_BLK0004L,Y
STA RAM_BLK0004H,Y
STA RAM_BLK0005L,Y
STA RAM_BLK0005H,Y
STA RAM_BLK0006L,Y
STA RAM_BLK0006H,Y
INY
BNE H2203

LDY #$2A ; copie infos volume sur block 2
H2226 LDA VOL_DIR_HEAD,Y
STA RAM_BLK0002L,Y
DEY
BPL H2226

* Effectue le chainage des blocks du volume dir

LDY #<$0002 ; previous block=2 on block 3
STY RAM_BLK0003L
INY
STY RAM_BLK0004L ; previous block=3 on block 4
INY
STY RAM_BLK0005L ; previous block=4 on block 5
STY RAM_BLK0003L+2 ; next block=4 on block 3
INY
STY RAM_BLK0004L+2 ; next block=5 on block 4

* Alimente la volume bitmap (block 6)

LDA VOL_TOT_BLK ; nbr de blocs * 8
STA $3C
LDA VOL_TOT_BLK+1
LSR
ROR $3C
LSR
ROR $3C
LSR
ROR $3C
CLC
ADC #>RAM_BLK0006L ; + adr d{but volume bitmap
STA $3D

LDY #0 ; au dela de l'espace des blocks du volume
TYA ; force les 8 blocks suivant @ l'{tat occup{
H225C STA ($3C),Y ; acc=0 (used) ou $FF (free)
LDA $3C ; octet de la volume bitmap pr{c{dant
SEC
SBC #$01
STA $3C
LDA #$FF ; 8 blocks libres
BCS H225C ; tout le reste est free

DEC $3D
LDX $3D
CPX #>RAM_BLK0006L
BCS H225C ; fait toute la volume bitmap en "free"
; pour finir:
LDA #%00000001 ; signale que les blocks 0 @ 6 sont bloqu{s dans
STA RAM_BLK0006L ; la volume bitmap.
; Vous noterez que rien n'a {t{ fait sur les blocks
; 0 et 1 de l'unit{ ramdisk

*-------------------------------
* Mise en place du driver
*-------------------------------

H2276 LDA #0
STA MAINWRT
STA MAINZP
STA BANKSEL ; bank 0 carte ram
BIT $C08B ; bank 1 -> utilise MEV (RAMDISK_DRV on)
BIT $C08B ; first bit: read allowed. Second bit: write allowed.

LDA #$D8 ; check 'CLD' (ID d{but driver)
CMP RAMDISK_DRV ; un driver d{ja en place? En MEV princ par d{faut
BEQ H229E ; oui

STA AUXZP ; MEV bank aux 0. Au cas o| le driver a {t{ mis
CMP RAMDISK_DRV ; en ram aux bank 0 pour une raison quelconque...
BEQ H229E ; oui. On garde page 0 aux et carte langage aux.

CMP $DE00 ; routine MLI main entry point en place en ram aux?
BEQ H229E ; oui

STA MAINZP ; recopie le driver en MEV princ

H229E LDY #0
H22A0 LDA H23CE,Y
STA RAMDISK_DRV,Y ; RAMDISK_DRV en carte langage
INY
CPY #$EB
BCC H22A0 ; si Y < #$EB

LDY DEVCNT ; recherche dans les devices actives le slot/drive
H22AE LDA DEVLST,Y ; d{fini pour le mapping du disk virtuel
LSR ; format device=DSSSIIII
LSR ; -> 0000DSSS
LSR
LSR
CMP MAP_SLOT_DRV ; est-ce cette device?
BEQ H22CC ; oui

DEY ; passe @ la device pr{c{dante
BPL H22AE
; la device n'est pas active
INC DEVCNT ; on l'ajoute @ la liste

LDY DEVCNT ; d{calage des devices pour inclure la nouvelle
H22C3 LDA DEVLST-1,Y
STA DEVLST,Y
DEY
BNE H22C3
; Y=0
H22CC LDA MAP_SLOT_DRV ; transforme 0000DSSS en DSSS0000
ASL ; D=0 -> drive 1, D=1 -> drive2
TAX ; x=device*2 (si S3 D1, X=6 -> DEVADR31)
ASL
ASL
ASL
STA ONLINE_NUM
ORA #%00001110 ; DSSS1110 -> 1110=no err sur la device
STA DEVLST,Y ; ajoute/modifie @ la liste des devices actives
LDA #<RAMDISK_DRV+1 ; pointeur driver pour cette device (skip CLD)
STA DEVADR01,X ; adr low driver
LDA #>RAMDISK_DRV+1
STA DEVADR01+1,X ; adr high driver
LDA MAP_SLOT_DRV
CMP #%00001011 ; slot 3, drive 2?
BEQ H2321 ; skip traitement slot 3 drive 2

* Traitement du slot 3, drive 2 : d{connection
* d'un driver RAM @ cet endroit

LDY DEVCNT ; recherche si cette device est active
H22F0 LDA DEVLST,Y
AND #%11110000 ; vire statut device
CMP #%10110000 ; slot 3, drive 2?
BEQ H22FE ; ok, found

DEY ; device pr{c{dante
BPL H22F0 ; pas fini

BMI H2321 ; branche toujours si not found

H22FE LDX DEVADR32+1 ; check driver ram en $FFxx
INX ; $FF+1=0
BNE H2321 ; pas un driver ram classique!

H2304 LDA DEVLST+1,Y ; supprime la device de la liste des actives
STA DEVLST,Y
INY
CPY DEVCNT
BCC H2304

BEQ H2304

DEC DEVCNT ; une device de moins
LDA DEVADR01 ; met la gestion de la device 0 @ la place
STA DEVADR32 ; low
LDA DEVADR01+1
STA DEVADR32+1 ; high

*-------------------------------

H2321 BIT $C082 ; lecture rom

JSR MLI ; recherche nom de ce volume @ partir de l'unit{
DFB ONLINE
DA TBL_ONLINE

LDX #0 ; check result
LDA ONLINE_BUF ; 1er octet buffer
ORA H239F
BNE RAMDISK_QUIT ; un caract}re trouv{ -> ok

BCC RAMDISK_QUIT ;

LDA #$FF ; anomalie ONLINE sur volume
STA H239F
STA AUXZP
LDA BANK_OK ; sur 1er bank valide
STA BANKSEL
STX $06 ; kill signature 1er bank aux valide
STX BANKSEL ; repasse sur bank 0 aux
STX VOL_TOT_BLK ; et nbr de blocks low
JMP H20EC ; remet le couvert...

*-------------------------------
* Lance le prochain SYS
*-------------------------------

RAMDISK_QUIT STA MAINZP
JMP $BD00 ; charge le prochain SYS



********************************
* *
* RWRDB *
* Read/Write RamDisk Block *
* Routine recopi{e dans chaque *
* bank aux valide en page 0 *
* (de $00B0 @ $00F4) *
* *
********************************

* Cette routine est exclusivement appel{e par
* CALL_RWRDB quand ce dernier est dans le buffer clavier
* en ram princ.

* In : carry = 1 : read
* carry = 0 : write
* Acc : high buffer ram princ
* X : low buffer ram princ
* Y : adr high ram aux d{but datas
*
* Out: Y = 0 : always
* carry = 0 : always
* N = 0 : req. ZP princ
* N = 1 : req. ZP aux

H2355
ORG $00B0

RWRDB
STA H00DE+2 ; sta high d{but adr ram princ
BCS H00B7 ; si read

STY H00DE+2 ; sta high d{but adr ram aux si write
TAY ; inverse de read

H00B7 LDA #0
STA AUXWRT
BCC H00C7 ; write -> {criture bank aux

TXA ; read -> lecture bank aux, {criture main mem
LDX #0
STA MAINWRT ; read: les donn{es vont de /ram vers la main mem
STA AUXREAD

H00C7 STY H00DB+2 ; lda high d{but block
INY
STY H00E1+2 ; lda high fin block
STA H00DE+1 ; sta low d{but block
STA H00E4+1 ; sta low fin block
STX H00DB+1 ; lda low d{but block
STX H00E1+1 ; lda low fin block
LDY H00DE+2 ; sta high
INY
STY H00E4+2 ; sta

* Effectue le read ou le write

LDY #0
H00DB LDA RAM_BLK0007L,Y ; LDA $??00,Y d{but block
H00DE STA RAM_BLK0007L,Y ; STA $????,Y
H00E1 LDA RAM_BLK0007L,Y ; LDA $??00,Y fin block
H00E4 STA RAM_BLK0007L,Y ; STA $????,Y
INY
BNE H00DB
; Y=0
STA MAINWRT ; {criture en princ
STA MAINREAD ; lecture en princ
CLC
BIT SAVE_ALTZP ; N=1 si ZP aux, N=0 si ZP princ
RTS

ORG $239A ; ORG alone -> BUG in generated code (bad ref addrs!)

*-------------------------------

* Parameters Online

TBL_ONLINE DFB 2
ONLINE_NUM DFB %00110000 ; unit %DSSS0000
DA ONLINE_BUF ; 16 octets $0220-022F

*-------------------------------

H239E DFB 0 ; nbr de banks valides-1
H239F DFB 0 ; pour ORA sur 1er caract}re buff r{sultat Online

* Signature en page 0 1er bank aux valide ($06/$07/$08)

H23A0 ASC "G" ; Glen
ASC "E" ; E.
ASC "B" ; Bredon

*-------------------------------

* Volume directory header

VOL_DIR_HEAD DA $0000 ; no previous dir block
DA $0003 ; next dir bloc

DFB %11110011 ; 4 first bits = $F = vol dir header id
; 4 last bits = $3 = volume name length
ASC 'RAM' ; volume name
DS 12,0 ; compl{ment nom pour 15 caract}res

DS 8,0 ; not used

VOL_DATETIME DS 4,0 ; creation date/time
DFB 1 ; version
DFB 0 ; min version
DFB %11000011 ; access: destroy/rename/write/read
HEX 27 ; lenght of each entry in bytes ($27 = standard)
HEX 0D ; entries per block ($0D = standard)
DA $0000 ; file count
DA $0006 ; bit map pointer (block 6)
VOL_TOT_BLK DFB 0,0 ; total block low/high


********************************
* *
* Driver RamDisk *
* recopi{ en MEV princ dans le *
* banc commut{ en $FF00-$FFEA *
* *
********************************

* Commande STATUS
* ---------------
* In : DRV_COMMAND : 00 (commande)
*
* Out: X : blocks dispos low
* Y : blocks dispos high
* carry : 0 (always) -> no err
* acc : 0 (always) -> no err
*
* Commande FORMAT
* ---------------
* In : DRV_COMMAND : 03 (commande)
*
* Out: carry : 0 (always) -> no err
* acc : 0 (always) -> no err
*
* Commandes READ/WRITE
* --------------------
* In : DRV_COMMAND : 01 (read) or 02 (write)
* DRV_BUFFL : low buffer implantation block
* DRV_BUFFH : high buffer
* DRV_BLKL : low block ProDOS 512 octets
* DRV_BLKH : high block ProDOS 512 octets
*
* Out: carry : 0 (no err) or 1 (err -> see acc)
* acc : 0 (no err) or $27 (I/O err)

H23CE

ORG $FF00

RAMDISK_DRV
CLD ; valid driver for BI
LDA DRV_COMMAND ; commande status?
BNE HFF0C ; non

ORG

*-------------------------------
* Commande : Status
*-------------------------------

* Retourne la taille calcul{e lors du formattage

; acc=0=no err
RWB_ST_BLKL LDX #$00 ; blocks dispos low
RWB_ST_BLKH LDY #$00 ; blocks dispos high

ORG $FF09

HFF09 CLC ; no err
BCC HFF83 ; toujours ->fin

*-------------------------------
* Commande : Format (NO EFFECT)
*-------------------------------

HFF0C CMP #$03 ; commande format?
BEQ HFF09 ; oui. Mais pas autoris{e sur le ramdisk
; carry=0 si commande 1 (read) ou 2 (write)
; carry=1 pour autres valeurs

*-------------------------------

HFF10 LDA #$27 ; I/O err
BCS HFF87 ; si commande > 3

*-------------------------------
* Commandes : Read / Write
*-------------------------------

* Sauvegarde avant utilisation

LDA R80ST ; m{morise {tat 80STORE
PHA
STA _80STORE0 ; utilise ramread/ramwrite
LDA $40 ; sauvegarde octets utilis{s page
PHA
LDA $41
PHA

LDA DRV_BUFFL ; ($40) pointe sur dernier 256 octets buffer ram
STA $40 ; princ pour la phase de swap buffer s'il n'est
LDX DRV_BUFFH ; pas bon
INX
STX $41

JSR CHECK_BUFFH ; routine copie CALL_RWRDB dans buffer clavier et
; check buffer high block
STX H02D3+1 ; met en place LDA#>buffer dans CALL_RWRDB
LDA RZPSW ; store {tat ALTZP (page 0 princ ou aux)
STA SAVE_ALTZP

LDA DRV_BLKH ; sauve block high
PHA
TAX ; X=block high

* Routine tr}s importante!!!!
* A partir d'un num{ro de block, calcule l'adresse en ram
* o| se trouvent les datas et le bank @ s{lectionner.
* Commute aussi la MEV et le bank de 4k si c'est cette
* partie qui est concern{e.

; Y=0
LDA DRV_BLKL ; enl}ve $7F blocks, c'est @ dire le nbr de blocks
HFF3C SEC ; contenus dans un bank
HFF3D INY ; on n'oublie pas qu'il faut prendre en compte le
SBC #$7F ; fait que le block 0 commence @ $0200

BCS HFF3D

DEX ; block high-1
BPL HFF3C
; carry=0
TYA ; ajout pour calc adresse
ADC DRV_BLKL
BCC HFF4C

INC DRV_BLKH

HFF4C ASL ; block restant * 2 = adresse
TAY ; dans Y
LDA DRV_BLKH
ROL
TAX ; num{ro bank

PLA ; restaure block high
STA DRV_BLKH

ORG

HFF55 CPX #$00 ; compare avec nbr de banks valides

ORG $FF57

BCS HFF74 ; trop grand -> reswap et I/O err

TYA ; examine adresse high
SBC #>$BF00
CMP #>$1000 ; banc commut{ 2 4k?
BCS HFF66 ; non

ADC #>$D000 ; skip $C000-$CFFF
TAY
BIT $C083 ; bank 2 4k MEV (read)

; Y=adr mem high
; X=bank @ prendre

HFF66 LDA DRV_COMMAND
LSR ; carry=cmd (=1 pour read, =0 pour write)
LDA RWB_RAM_BK,X
LDX DRV_BUFFL
JSR CALL_RWRDB ; lance les op{rations d'E/S
BIT $C08B ; bank 1 4k MEV (read)

HFF74 JSR CHECK_BUFFH ; remet la situation initiale en place dans le
; buffer clavier

* Restaure les valeurs alt{r{es

PLA ; octets page 0
STA $41
PLA
STA $40
PLA ; restaure {tat 80STORE
BPL HFF83 ; ok d{ja usage ramread/ramwrite

STA _80STORE1 ; acc}s page affichage


*-------------------------------

HFF83 LDA #0 ; no err
BCS HFF10 ; I/O err
; carry=0
HFF87 RTS ; end no err

*-------------------------------
* Swap routine appel RWRDB et
* contenu espace buffer clavier
* et check adr high buffer
*-------------------------------

* Il faudra 2 appels de cette routine:
* 1 pour mettre en place la routine en $02D0
* + backup buffer clavier
* 1 pour remettre la situation initiale

* In : DRV_BUFFH = buffer high main memory
* DRV_BUFFL = buffer low
* ($40) = pointe sur les 256 derniers octets buffer
*
* Out: X = buffer high ram princ valid{
* Y = 0 (always)
*
* carry n'est pas alt{r{e

CHECK_BUFFH
LDY #$15 ; met en place $02D0-$02E4 en ram princ dans le
HFF8A LDX HFFD6-1,Y ; buffer clavier et en meme temps sauve le contenu
LDA CALL_RWRDB-1,Y ; de ce dernier @ la place du code d{plac{.
STA HFFD6-1,Y
TXA
STA CALL_RWRDB-1,Y
DEY
BNE HFF8A

* Il est interdit d'utiliser l'espace >= $BC00
* comme buffer. Si dans l'espace interdit, alors force
* le buffer sur $8000-$81FF.

; Y=0. Default X=buffer high demand{
LDX DRV_BUFFH ; si buffer < $80xx
BPL HFFBD ; buffer high ok
; >=$8000
BIT DRV_BUFFH ; si buffer < $BCxx
BVC HFFBD ; V=0 : buffer ok

HFFA2 LDX $8000,Y ; swap contenu buffer demand{ avec buffer
LDA (DRV_BUFFL),Y ; autoris{
STA $8000,Y ; buffer d{but block
TXA
STA (DRV_BUFFL),Y
LDX $8100,Y ; buffer fin block
LDA ($40),Y
STA $8100,Y
TXA
STA ($40),Y
INY
BNE HFFA2
; Y=0
LDX #>$8000 ; force buffer $8000
HFFBD RTS

*-------------------------------

RWB_RAM_BK DS $18,0 ; $FFBE-$FFD5 (et $248C-$24A3) : liste des banks
; valides s{lectionn{s.

********************************
* *
* Routine d'appel de RWRDB *
* stock{e dans buffer clavier *
* de la ram princ *
* *
********************************

* In : carry = 1 read
* carry = 0 write
* acc = bank aux @ commuter
* Y = adr high ram aux d{but datas
* X = adr low buffer valide ram princ
* $02D3+1 = adr high buffer valide ram princ

HFFD6
ORG $02D0

CALL_RWRDB
STA BANKSEL ; enclenche bank = acc
H02D3 LDA #0 ; LDA#>buffer ram princ
STA AUXZP ; carte langage main mem off (RAMDISK_DRV off)
; et RWRDB bank aux on (page 0 bank aux on)
JSR RWRDB ; passe par la routine en page 0 aux
; Y=0
STY BANKSEL ; bank 0
BMI :1 ; N=1 -> keep ZP aux (cas sp{cial driver en bank 0
; aux)
STA MAINZP ; RAMDISK_DRV on et page 0 ram princ
:1 RTS

SAVE_ALTZP DFB 0 ; sauvegarde switch page 0 (princ ou aux)
; $02E4

SAV RAM.DRV.TST
Re: Ramworks, Checkmate et al. [message #242096 is a reply to message #242034] Mon, 03 February 2014 18:37 Go to previous messageGo to next message
Steven Hirsch is currently offline  Steven Hirsch
Messages: 798
Registered: October 2012
Karma: 0
Senior Member
On 02/03/2014 10:59 AM, mdj wrote:

> Clones of the RamWorks cards respond to a bank register change on a write
> to any address in $C07X, the range that fires the paddle strobe. If you
> look at the signal availability on the Auxiliary slot it becomes apparent
> why this is; it's the only way to do it. The RamWorks however has gone to
> particular trouble to respond only to a write on address $C073. I had
> originally overlooked this for the purposes of getting the design working
> with the intention to revisit, but having given it some thought I'm not
> entirely sure which way I should go.
>
> The only purpose AE's design tweak seems to serve (in addition to being
> less susceptible to a phantom bank change) is actually in providing a
> measure of copy protection for their software. The RAM test fails on a
> non-AE card (mind included) and their provided Ramdisk software also fails
> to run. This is done by producing a phantom write to another $C07X address
> that on other cards changes the current bank but on the RamWorks card does
> nothing. Of course, programs like AppleWorks recognise the memory just
> fine.

You have that exactly right. I was doing contract work for them at the time
and recall several conversations where they asked me to ensure that RamWorks
drivers would fail on third-party boards (CheckMate was a particular irritant
to them, IIRC).
Re: Ramworks, Checkmate et al. [message #242112 is a reply to message #242077] Mon, 03 February 2014 23:17 Go to previous messageGo to next message
mdj is currently offline  mdj
Messages: 301
Registered: December 2012
Karma: 0
Senior Member
On Tuesday, 4 February 2014 06:28:30 UTC+10, gid...@sasktel.net wrote:
> Does anyone have that, or know of any other RamWorks-style RAM drive software I may have missed?

<snip>

Thankyou! Quite how I've used ProSEL for over 25 years and not noticed that before is a worry. I guess we see what we want to see and not necessarily what's there :-)
Re: Ramworks, Checkmate et al. [message #242119 is a reply to message #242096] Tue, 04 February 2014 00:08 Go to previous messageGo to next message
mdj is currently offline  mdj
Messages: 301
Registered: December 2012
Karma: 0
Senior Member
On Tuesday, 4 February 2014 09:37:03 UTC+10, Steven Hirsch wrote:

> You have that exactly right. I was doing contract work for them at the time
> and recall several conversations where they asked me to ensure that RamWorks
> drivers would fail on third-party boards (CheckMate was a particular irritant
> to them, IIRC).

Thanks. I haven't exhaustively tested it but I'm hopeful that the RamWorks actually bank switches on C071,3,5,7. AE's example code does this:

BankSel EQU $C073

; Then in the find loop

STA BankSel
STA BankSel+3

Not exactly super obtuse. I'll do some testing on a real RamWorks to confirm. I suspect this is the case because that way they only had to decode RA0 during /RAS, rather than RA0-3. If that's the case I think I can squeeze that into the 'spare' logic in my design without having to add any more centipedes, which would be nice.

Matt
Re: Ramworks, Checkmate et al. [message #242126 is a reply to message #242059] Tue, 04 February 2014 02:52 Go to previous messageGo to next message
Bill Garber is currently offline  Bill Garber
Messages: 507
Registered: December 2011
Karma: 0
Senior Member
"STYNX" <Jonas.Groenhagen@gmx.de> wrote in message
news:3ce2e5b0-acb6-4964-821f-dcf1043bdc7e@googlegroups.com...
> On Monday, February 3, 2014 4:59:17 PM UTC+1, mdj wrote:
>> Howdy,
>> As some of you would be aware I recently designed a clone of
>> the Applied Engineering RamWorks card to show off at OzKest.
>
> Interesting, to say the least :-)
>
> You might be interested in asking this Guy
> http://www.ebay.de/itm/160968650489 for chips.
> He offered me $1.50/pc at 40pcs minimum volume per order.
> I haven't seen them cheaper anywhere else. They are only 8mbit
> (1mx8) 55ns though and therefore too slow for my projects.
>
> -Jonas

Good Lord, Jonas, how fast do you need them? 25ns???

Bill Garber
http://www.sepa-electronics.com
Re: Ramworks, Checkmate et al. [message #242128 is a reply to message #242061] Tue, 04 February 2014 03:24 Go to previous messageGo to next message
mdj is currently offline  mdj
Messages: 301
Registered: December 2012
Karma: 0
Senior Member
On Tuesday, 4 February 2014 05:19:08 UTC+10, STYNX wrote:
> after i posted the hint to the ebay guy, i reread your post. I think that there is no 'right way'. Personally i like the linear addressing more as it seems a little more 'neat'. A nice (and cheap) 2mb (or bigger) memory expansion might be cool, especially if it can be buffered with a button cell for several months or even years. A fun project indeed ;-)

It has been fun to do, and doing it on a breadboard allowed me to apply 'agile development' to hardware that I otherwise might've over-engineered.

PCB layout is a lot less 'fun' than actually making new(old) things, but that's mainly because the software tooling involved is so god-awful bad.

Matt
Re: Ramworks, Checkmate et al. [message #242167 is a reply to message #242126] Tue, 04 February 2014 11:44 Go to previous messageGo to next message
STYNX is currently offline  STYNX
Messages: 453
Registered: October 2012
Karma: 0
Senior Member
On Tuesday, February 4, 2014 8:52:20 AM UTC+1, Bill Garber wrote:
> "STYNX" <Jonasxxx@gmx.de> wrote in message
>> (1mx8) 55ns though and therefore too slow for my projects.
> Good Lord, Jonas, how fast do you need them? 25ns???

At least 35ns for a Memory board that can be plugged directly into a 386 socket and should have 4Mb zero wait-state ram. It is for the Intel inboard I own (with a 25mhz bus and 50mhz CPU).

-Jonas
Re: Ramworks, Checkmate et al. [message #242180 is a reply to message #242096] Tue, 04 February 2014 12:31 Go to previous messageGo to next message
scott is currently offline  scott
Messages: 4237
Registered: February 2012
Karma: 0
Senior Member
In article <n-ednftD076CtW3PnZ2dnUVZ_qEAAAAA@giganews.com>,
Steven Hirsch <snhirsch@gmail.com> wrote:
> You have that exactly right. I was doing contract work for them at the time
> and recall several conversations where they asked me to ensure that RamWorks
> drivers would fail on third-party boards (CheckMate was a particular irritant
> to them, IIRC).

AE took out a patent on the RamWorks boards:

http://www.google.com/patents/US4601018.pdf

The "bank selection decoder" block (in the diagram on page 1) is connected
to both /C07X and the address bus. I've not dug too deep into the details,
but how that block was implemented is most likely how they would've tried to
lock out cloners.

_/_
/ v \ Scott Alfter (remove the obvious to send mail)
(IIGS( http://alfter.us/ Top-posting!
\_^_/ >What's the most annoying thing on Usenet?
Re: Ramworks, Checkmate et al. [message #242187 is a reply to message #242180] Tue, 04 February 2014 13:41 Go to previous messageGo to next message
Wayne Stewart is currently offline  Wayne Stewart
Messages: 306
Registered: September 2012
Karma: 0
Senior Member
Didn't seem to work that well, at least in Canada. I've had a couple of RamWorks clones pass through my hands.

On Tuesday, February 4, 2014 9:31:54 AM UTC-8, Scott Alfter wrote:
>
> AE took out a patent on the RamWorks boards:
>
>
>
> http://www.google.com/patents/US4601018.pdf
>
>
>
> The "bank selection decoder" block (in the diagram on page 1) is connected
>
> to both /C07X and the address bus. I've not dug too deep into the details,
>
> but how that block was implemented is most likely how they would've tried to
>
> lock out cloners.
>
>
>
> _/_
>
> / v \ Scott Alfter (remove the obvious to send mail)
>
> (IIGS( http://alfter.us/ Top-posting!
>
> \_^_/ >What's the most annoying thing on Usenet?
Re: Ramworks, Checkmate et al. [message #242224 is a reply to message #242187] Tue, 04 February 2014 21:39 Go to previous messageGo to next message
Steven Hirsch is currently offline  Steven Hirsch
Messages: 798
Registered: October 2012
Karma: 0
Senior Member
On 02/04/2014 01:41 PM, waynejstewart@gmail.com wrote:

> Didn't seem to work that well, at least in Canada. I've had a couple of
> RamWorks clones pass through my hands.


Heh. Canada was "Land of the Clones" during the 80s. I used to do a bunch of
coding with a guy from Ottawa. During a period of time when US Customs was
zealous about seizing inbound Apple 2 clones they were plentiful up there. I
recall Rod taking me to a computer store where you could get Taiwanese
knockoffs of anything you could think of.

As a bit of retro fun, I recently picked up a "no-name" IIe clone on eBay that
came from the chilly North. Absolutely no marking to indicate where it was
made and the splash screen says "COMPUTER" during boot.


Steve
Re: Ramworks, Checkmate et al. [message #242225 is a reply to message #242180] Tue, 04 February 2014 23:14 Go to previous message
mdj is currently offline  mdj
Messages: 301
Registered: December 2012
Karma: 0
Senior Member
On Wednesday, 5 February 2014 03:31:54 UTC+10, Scott Alfter wrote:

> The "bank selection decoder" block (in the diagram on page 1) is connected
>
> to both /C07X and the address bus. I've not dug too deep into the details,
>
> but how that block was implemented is most likely how they would've tried to
>
> lock out cloners.

Interesting. I hadn't looked at that as I wanted to do my own design. It does confirm that the Bank Select register operates on C071,3,5,7 which is to say A0 AND /A3.

As luck would have it, the redundant gates in my design at this stage are 3 inverters and 3 OR gates. The address lines are already buffered during /RAS since it's an SRAM card. Since I can implement AE's 'trick' using nothing but wire and it would get the design extremely close to 100% utilisation of the centipedes, I think I have an answer to my question :-)
  Switch to threaded view of this topic Create a new topic Submit Reply
Previous Topic: File type list
Next Topic: What to lend for games and math
Goto Forum:
  

-=] Back to Top [=-
[ Syndicate this forum (XML) ] [ RSS ] [ PDF ]

Current Time: Fri Apr 19 06:20:54 EDT 2024

Total time taken to generate the page: 0.16752 seconds