Jump to content
IGNORED

SAMS software control


marc.hull

Recommended Posts

SAMS as implemented in MAME:

 

https://github.com/mamedev/mame/blob/master/src/devices/bus/ti99/peb/samsmem.cpp

 

In short, if you don't like to read cpp code:

  • There are two CRU bits (1E00 and 1E02); the first one allows access to the mapper, the second enables mapping (otherwise the memory locations are the logical addresses as issued on the address bus).
  • When the mapper access is enabled, memory locations 4000,4002,4004,...401e can be loaded with a byte value that represents the bank number. The 16 slots of the mapper represent the 16 pages of 4K in the address space (0000, 2000, ..., E000).
  • Only the logical addresses 2000-3FFF and A000-FFFF are used. Thus, the mapper has a lot of unused positions; only 4004,4006,4014,4016,4018,401a,401c,401e have an effect.

The SAMS can be equipped with 1 MiB (two 512K RAMs). This allows for 256 pages of 4K, numbered from 00 to FF. By loading the page number into the mapper, it shows up in the respective area.

 

Due to a lack of test software, I think I never really tested it throughly. Maybe someone would like to try with this information?

  • Like 1
Link to comment
Share on other sites

I seem to recall that Bruce Harrison wrote some good introductory info in a Micropendium Art of Assembly column.

 

Yeah—He discussed it in Parts 77, 78 and 80. Those parts were submitted to Micropendium but never published. A PDF of Harrison’s “Art of Assembly” columns is available at WHTech.

 

...lee

  • Like 1
Link to comment
Share on other sites

The Geneve section of WHTECH is full of good stuff too... Cause??? ( just like most of the DSK files are under videos... )

I was just browsing and spotted this:

----------------------------------------
          AAAA   M    M   SSSSS
         A    A  MM  MM  S
         A    A  M MM M  S
         AAAAAA  M    M   SSSS
         A    A  M    M       S
         A    A  M    M       S
         A    A  M    M  SSSSS
----------------------------------------
 
       Programmer's Documentation
          Documention: 1/19/93
               Joe Delekto
 
Note: This documentation covers the 128k
      AMS system only.  After the AEMS
      is released, documentation will
      be available.
 
----------------------------------------
 
   The AMS expanded memory card is a
unique piece of hardware, in that
mapping is simple, and lends itself
well to overlay-structured programming.
 
   Because of the AMS design, no memory
manager is necessary.  The card itself
uses a 17 bit address bus (18 for 512k)
in order to access the SRAM on the
card.  The upper 4 bits of a standard
16 bit address are used to select one
of 16 mapper registers.  The remaining
12 bits from the address bus are
combined with 12 bits taken from the
mapper register, in order to give a
maximum address bus of 24 bits in
width.  This will allow for a maximum
of 16MB to be accessed.  (AEMS)
 
   On the 128k card, only the 5 least
significant bits (6 for 512k) are used
from the mapper register.  The other
pins from the mapper output are
unconnected.  "Mapping" is accomplished
by changing the value in the mapper
register, to point to one of 32 pages
(64 on 512k).  No reading/writing or
any transfer of memory is involved with
mapping.  All that is changed is the
pointer to the RAM chip on the address
bus.
 
   Because of this, mapping can be done
in a few clock cycles, using only a
couple of instructions.  Programs which
take advantage of the AMS can be
extremely large, with about no change
in execution time.  As with any memory
expansion, there are limitations on
overlay sizes.
 
   It is recommended for the AMS
system, that the root segment of your
program be placed in low memory.
(>2000 - >3FFF)  Code overlays, from 4k
to 24k in size, (in 4k increments) can
be mapped in within the upper 24k of
memory.  This means that an 8k root
segment can call as many (up to 24k)
overlays as necessary.  The result is a
HUGE program, with structure and
modularity.  (Most desired in the
programming field!)
 
   This document will describe how
mapping works, as well as the AMS
resident library created by Art Green
and myself.  I will also go into some
detail as to how you can use Charles
Earl's Hot Bug debugger to debug AMS
code.  Before I get to the meat of this
document, I would like to explain why
we chose our method of memory
expansion.
 
   We chose 4k pages for two reasons.
First, it made the hardware design
simple, and made utility routines short
and fast.  Second of all, since our
system uses overlay methods, many 4k
overlays can reside in memory at once.
The overlays can be any size from 4k to
24k in length, falling on 4k page
boundaries.  The larger the overlay is,
the less overlays you can have.  You
can have 4k, 8k, 12k, 16k, or 24k
overlays, and overlay size determines
how many overlays you can have.  (i.e.
Six 4k, three 8k, etc.)
 
   You are NOT required to use only one
size of overlay.  For instance, you
could have one 12k overlay, and an 8k
overlay, along with a 4k overlay. (24k
total).  Keep in mind that most
subroutines fall well under 4k of
space!  This means that MANY
subroutines can be placed within just
one 4k overlay!
 
   We believe you will find this to be
one of the most flexible memory
expansion systems, ever to be designed
for the TI-99/4A.  Many interesting
applications, besides large programs,
can be developed.  We are making all
information on the use of the memory
available, so that programmers can make
full use of its abilities.
 
----------------------------------------
 
PART ONE:  Map Modes and Registers
==================================
 
   Because the AMS has a 17 bit address
bus, (18 for 512k) and the TI-99/4A
only has a 16-bit address bus, the
extra bit(s) need to come from
somewhere.  These extra bits are taken
from the mapper registers.  4 bits are
taken from the memory address on the
9900 bus, and used to select one of 16
mapper registers.  The remaining 12
bits from the 9900 bus are combined
with the 5 bits from the mapper
register, to make a new bus with 17
bits.  The actual paging process is
done by changing the values in the
mapper registers, to point to new pages
in memory.  Here is the address
diagram:
 
* From 9900 Address bus:
 
>A000
 |+--
 ||
 |+---> 12 MSB to 12 LSB of new address
 |
 +----> 4 MSB to mapper register select
 
* From mapper
 
 5 bit output forms 5 MSB of new address
 
A15 - Mapper Register Select
A14 - "    " "      " "    "
A13 - "    " "      " "    "
A12 - "    " "      " "    "
 
      Mapper Address Bus
 
A11 -       MA11
A10 -       MA10
A09 -       MA09
A08 -       MA08
A07 -       MA07
A06 -       MA06
A05 -       MA05
A04 -       MA04
A03 -       MA03
A02 -       MA02
A01 -       MA01
A00 -       MA00
 
* On Mapper Address bus:
 
     +---+
A15 -| M |- MA16
A14 -| a |- MA15
A13 -| p |- MA14
A12 -| p |- MA13
     | e |- MA12
     | r |
     +---+
 
   Since only the >A000 - >F000 range
inside memory is mapped, register >A
through >F are used in the mapper.
Note the mapper register number
corresponds with the 4 MSB of the
address being accessed.
 
   Once the mapper register is loaded
with a page number, (5 bit address
which forms 5 MSB on AMS bus) any read
or write to the 4k block it corresponds
to, will access that 4k memory page.
 
   For example:  I load mapper register
10 (>A) with page number >15.  Any time
I read/write or access the >A000 block,
I will be writing to the 4k page >15.
 
   If I were to load mapper register
10 with >15, then perform a CLR @>A02E,
I will actually be writing (from the
AMS address bus) the address:
>0001502E.  Notice how the page/offset
are combined on the AMS bus to get a 17
bit address (show here as 32 bits for
clarity).
 
   It will be worthwile to note, that
even though the address was >A000, the
>A had no influence whatsoever on the
new address.  The primary purpose of
the >A was to select which mapper
register the 5 bits will come from.
The same holds true for the entire
>A000 - >FFFF range.  Because >A - >F
are used to select the mapper
registers, we have 6 registers to use,
and 6 4k boundaries.  To load
consecutive addresses, just load
the consecutive page numbers into the
mapper registers.
 
   The AMS system works in two modes.
map mode, and pass mode.  Power-up
places AMS into pass mode.  When the
mode is in pass, AMS acts as a plain
32k card, and the mapper passes actual
addresses used to the AMS bus.  (i.e. a
CLR @>A000 will clear the memory
location at >A000).  There is no
difference between pass mode, and
standard 32k mode.  The second mode is
map mode.  Map mode is the mode which
uses the MSB of an address to select
the mapper register, then dump the
register's contents to the AMS bus.
Note, when map mode is enabled, it
would be a good idea to initialize the
mapper registers to known values!
 
   The CRU address for AMS is >1E00.
In order to use mapping, some CRU
instructions are required to: 1) Switch
AMS between map/pass modes, and 2)
enable/disable register read/writes.
Below is the code which changes map
modes for AMS.
 
 LI   R12,>1E00  * AMS CRU Address
 SBO  1          * Enable Mapping
 SBZ  1          * Disable Mapping
 
   This is the only code required to
switch back between modes.  Only 2
instructions are necessary.
 
   In order to access the mapper
registers, CRU bit 0 must be set.  When
it is, DSR space is temporarily
disable, so that writing in the >4000
space will set a map register.  When
CRU bit 0 is set to zero, original DSR
space is recovered, with no side
effects.  It is recommended that you
enable mapper registers, write their
values, and then disable them
immediately.  The reason being that
forgetting to disable the registers
will keep you from accessing and DSR
routines.  To enable and disable
registers, use the following code:
 
 LI   R12,>1E00  * AMS CRU address
 SBO  0          * Enable Registers
 .
 .               * Set registers here
 .
 SBZ  0          * Disable Registers
 
 
   In order to load a mapper register
with a page number, all you need to do
is write to the >4000 block.  To
determine which mapper register you
wish to change, use the follow
calculation:
 
   MRAD = 2 * Register# + >4000
 
   So that to clear mapper register 10,
you would use:
 
      2 * >A + >4000 = >4014
 
   CLR  @>4014  * Clear Register 10
 
   Note, you can also read from a
mapper register, for the purpose of
saving previous page values:
 
SPAGE BSS  2             * Hold Page #
 
EXMPL MOV  @>4014,@SPAGE * Get MR10
      CLR  @>4014        * Clear MR10
      .
      .
      .
      MOV  @SPAGE,@>4014 * Restore Page
      RT
 
   Because writing to the mapper
registers is just writing to an
address, indirect addressing could be
used as well.  For example, Consider
setting up the mapper so that when in
map mode, addresses are the same as in
pass mode.  It is always a good idea to
first set up the mapper registers, and
then go into map mode.  While in map
mode, registers can be changed at will
to point elsewhere.  If your code to do
mapping resides in upper memory, take
care NOT to change the register where
your code is executing.  Pointing to
some other place in memory will
continue execution on the new page,
causing undesired, or unknown results.
It is possible though, to point to a
new page where code is executing,
provided that valid code exists at the
current offset and new page.
 
   Below is an example which sets up
the mapper registers as normal 32k pass
mode, yet places the mapper into map
mode.  The registers can be changed
later to access other pages of memory.
 
PAGES DATA >0A00,>0B00,>0C00
      DATA >0D00,>0E00,>0F00
 
START LI   R12,>1E00   * AMS CRU
      LI   R1,PAGES    * Page Table
      LI   R2,>4014    * Start at MR >A
      LI   R3,6        * 6 Pages to set
 
      SBO  0           * Enable MR's
RSET  MOV  *1+,*2+     * Write to MR
      DEC  R3          * Dec counter
      JNE  RSET        * Continue
 
      SBZ  0           * Disable MR's
      SBO  1           * Enable map mode
      END
 
   IMPORTANT:  Note that the 5 bit page
value is placed in the most significant
byte of the mapper register.  Because
only 5 bits are used in a map register,
and because the 2 cycle read/write on
the data bus loads the most significant
byte last, the mapper is loaded with
this value.  Therefore, page >18 would
be >1800, page >05 would be >0500, etc.
It might be worthwhile that AEMS
addresses page numbers normally, since
12 bits are used instead.
 
----------------------------------------
 
PART TWO: Overlay Techniques
============================
 
   Because the AMS system is able to
map overlays up to 24k in length, on 4k
boundaries, it lends itself well to
program development using overlays.
 
   First, a root segment is
established, which will contain the
code to call an overlay.  The root
segment must remain in memory (without
using tricky code to map it out), and
will contain the routine used to call
an overlay subprogram.
 
   We recommend the following: 1) Place
the root segment into low memory (8k).
2) Make all overlay calls BLWP
routines.  Below is the stub code for
both the root segment overlay manager,
which is used to handle the simulation
of a BLWP vector for a mapped
environment.
 
****************************************
 
* Overlay Manager
* Version 1.0
* R.A.Green
 
OVMGR SBO  0         * Enable map regs
      MOV  *R11+,R10 * Get N # pages
      MOV  *R11+,R9  * Get 1st map reg
      MOV  *R11+,R7  * Get 1st page #
OMGR2 MOV  R7,*R9+   * Set mapper reg
      AI   7,>0100   * Add 1 to page #
      DEC  R10       * Loop for N pages
      JGT  OMGR2     * Finish loop
      SBZ  0         * Disable map regs
      MOV  *R11,*R11 * Get real BLWP vec
      MOV  *R11+,R7  * Get WSP
      MOV  *11,R9    * Get sub address
      MOV  R13,@26(R7)  * Simulate BLWP
      MOV  R14,@28(R7)  *
      MOV  R15,@30(R7)  *
OMGRW EQU  $-12      * OVMGR workspace
 
      LWPI 0         * R6,R7 user wrkspc
      B    @0        * R8,R9 call sub
      BSS  2         * R10
      BSS  2         * R11
      DATA >1E00     * R12 AMS CRU addr
      BSS  6         * R13 - R15
 
****************************************
 
   Below is the code which replaces the
original BLWP call in the root segment.
This is done for every subroutine that
generates an overlay.
 
* Overlay Call
* Version 1.0
* R.A.Green
* BLWP @OSUB
 
OSUB   DATA OMGRW  * Manager WSP
       DATA $+2    *
       BL   @OVMGR * Use overlay manager
       DATA N      * # Pages in overlay
       DATA >40xx  * 1st mapper reg addr
       DATA n      * 1st page number
       DATA sub    * REAL BLWP vector
 
****************************************
 
   In order to generate a call for an
overlayed subroutine, the real BLWP
call must be replaced by the OSUB
information.  Now, the overlay
generator needs to know: 1) How many
pages the overlay is.  Remember that it
can be 4k-24k in length, broken into 4k
pages.  The routine needs to know what
mapper registers to start mapping the
'N' pages in at.  The first page number
the overlay resides on is also given,
along with the ACTUAL BLWP vector
address for the overlayed routine.
 
   To illustrate this, let us say our
root segment is in the >2000 - >3FFF
block.  We have created an overlay, and
inside the overlay is a routine called
INPUT, for which the BLWP vector starts
at >C2E0.  The overlay is in page >18
of memory, and is only 4k, or 1 page in
length.  To call the overlay, we would
use the following code:
 
   BLWP @OSUB1  * Call overlay stub
   DATA >0001   * 1 page long (4k block)
   DATA >4018   * >C000 block
   DATA >1800   * 1st page # (only one)
   DATA >C2E0   * BLWP Vector
 
 
   Note, it would be very useful to
have a program loader to load segments
of code into different pages.  Although
such a loader exists for AMS, it is
only used for AMS files with special
headers for overlay and root segments.
A similair loader can be constructed,
which loads the overlays into their
corresponding pages.
 
   The overlay code examples above, are
the code segments installed
(automatically) by the linker.  That
eliminates the need for passing the
arguments to the overlay generator, and
keeping track of relative page
addresses.
 
   You may however, choose your own
method of overlaying.  We made it very
flexible to customize your software so
you can choose how you want to map.
Keep in mind that other programs may be
resident to AMS, and using the
linker/loader will ensure that AMS
programs are page relocatable, and
won't overwrite memory resident code.
 
----------------------------------------
 
PART THREE: Using Hot Bug with AMS
==================================
 
   Most often overlay programs are
tedious to debug.  If you have access
to Charles Earl's Hot Bug debugger, I
recommend you learn how to use it.  It
is by far one of the best debugging
utilities available, and can certainly
work well for debugging AMS programs.
 
   Since Hot Bug will also load program
files, you can use the debugger to
change the page map, and load in your
overlay code!
 
   Hot Bug Command Summary
   ER  - Edit Register
   EW  - Edit Word
   DM  - Display Memory
   SPC - Set Program Counter
   G   - Go (# of steps)
 
 
   In order to access the registers,
and check the code/data within pages,
we need to enable both the mapper, and
the registers.  Choose a word of memory
that does not have code to use the
following commands: (For this example,
I use >3FF0)
 
   1:  ER 12 1E00
   2:  EW 3FF0 1D00
   3:  EW 3FF2 1D01
   4:  SPC 3FF0
   5:  G 2
 
(1: Load Register 12 with CRU >1E00)
(2: Put SBO 0 at 3FF0 Enable REGS  )
(3: Put SBO 1 at 3FF2 MAP Mode     )
(4: Set program counter to >3FF0   )
(5: Execute 2 instructions         )
 
   NOTE: If the mapper is in an unknown
state, (register values unknown), you
will want to set the registers before
actually placing into map mode.  Just
use G 1 instead, edit the registers
(see below) and then G 1 again to get
into map mode.
 
   To read/write to the mapper
registers, use the DM command to look
at the >4000 block.  Only addresses
>4000 - >4020 are of interest to us.
(Mapper regs 0 - 16).
 
   NOTE: Even though the upper 24k is
mapped using mapper registers 10 - 16,
the other mapper registers can be used
for temporary storage.
 
   1: DM 4000
 
(1: Display Memory at >4000)
 
   In order to change a register value,
just use the EW command.  For example,
to load mapper register 11, (>B000
block) with page >15, use the
following:
 
   1: EW 4016 1500
 
(1: Load mapper register 11 with >15)
 
   Now let's try an experiment.  What
we will do is write the same page to 2
different mapper registers, and observe
what happens.  Use the following
commands:
 
   1: EW 4014 1500
   2: EW 4016 1500
   3: DM A000
 
(1: Load mapper register 10 with >15)
(2: Load mapper register 11 with >15)
(3: Display memory at >A000         )
 
   Note what the data in memory is at
>A000.  Now, if you use DM B000, you
should see the same data you saw
before.  Let's try something
interesting.  Use the following
commands:
 
   1: EW A000 FACE
   2: DM B000
 
(1: Put value >FACE into >A000)
(2: Display memory at >B000   )
 
   When you use the DM B000, you should
get a surprise.  When you wrote to
A000, you actually changed the word at
B000, as well as A000.  Why?  Because
both 4k block point to the same page!
Perhaps now you can envision some of
the interesting tricks you can
accomplish with the AMS system.  One
such application is the arbitrary
locating of data buffers!
 
   It is also possible to load a memory
image file, on non-consecutive pages,
and yet still load the mapper registers
such that the program is contiguous in
the upper 24k!  If that's so, then it
means we can load E/A option 5 program
files anywhere inside AMS, and then
just map in their pages to the proper
blocks in high memory!  In this manner,
even code with absolute origins becomes
page relocatable, at least for paging
purposes.
 
   By placing page numbers into the
registers, and using Hot Bug's LOAD
command, you can load overlay image
files.  Keep track of the address for
the BLWP vector in the overlay, as well
as the page you LOAD it into, and how
many pages it takes up.  This
information you will need to pass to
the overlay generator in your program.
 
   See?  Loading, debugging, and
running overlay code on the AMS system
is very feasible, and not difficult.
 
----------------------------------------
 
   This concludes this section of
programmer's documentation.  The next
document will focus on the memory
resident utility routines, which AMS
programmers can use in their software.
Memory allocation, exit code, memory
moves, and far VDP read/write routines
are available.  Also, AMS program have
access to the E/A 5, and AMS Overlay
program file loader.  The loader will
load either type of file.  The exit
routines for AMS have the option of
keeping the programmer's code resident
for instant execution when desired.
 
   We have worked very hard for the
past couple of years, to make this
memory expansion as user friendly as
possible.  We are, and will continue to
supply support for the AMS card.
Without the software support to use
AMS, it would just be an expensive
paperweight.
 
                     <<< Joe Delekto >>>

  • Like 4
Link to comment
Share on other sites

Does anyone know or have a tutorial on how to control the SAMS card with software ? Not interested in the hardware aspects just in how to manipulate them.

I have a solution easy to control built into RXB.

 

 

 

 

 

  • Like 2
Link to comment
Share on other sites

So here is what I do in my CRPG with the card, which I think should illustrate a 'hands on' approach:

 

When you're in memory mapping mode, all your memory is in "pages" so it's actually pretty easy to accidentally break a program you just started in when turning it on. In the loader program, I set up pages 0-15 in the respective memory addresses, which appears to allow a smooth transition to mapping mode. I don't know how completely necessary this is, but it's best to assume the card is not initialized.

* Set up AMS
       LI   R12,>1E00                  * Set CRU for AMS
       LI   R0,>4000
       CLR  R1
       LI   R2,16
INIT1  SBO  0                          * Turn on AMS card page mapping
       MOV  R1,*R0+                    * Copy page assignments (0-15)
       AI   R1,>0100                   * Add 1 to each page value
       DEC  R2
       JNE  INIT1                      * Are we at end of loop?
       SBZ  0                          * Turn off memory page mode
       LI   R12,>1E00                  * Set CRU for AMS
       SBO  1                          * Turn on mapping mode

After loading all my program data into various pages in the loader, I set up half of the upper 24k to a fixed "root" module, which is always present, and the second half to a "start" module which has the title screen, music, character creation, etc.

* Setup root segment, branch to start segment
INIT3  LI   R12,>1E00                  * Access AMS card mapper
       SBO  0
       LI   R5,>0A0A                   * Set R5 to 10 (high byte)
       MOV  R5,@>4014                  * Set >A000 page for Root segment
       AI   R5,>0100                   * Add 1 to page
       MOV  R5,@>4016                  * Set >B000 page for next file
       AI   R5,>0100                   * Add 1 to page
       MOV  R5,@>4018                  * Set >C000 page for next file
       LI   R5,>1600                   * Set R5 to 22 (high byte)
       MOV  R5,@>401A                  * Set >D000 page for Start segment
       AI   R5,>0100                   * Add 1 to page
       MOV  R5,@>401C                  * Set >E000 page for next file
       AI   R5,>0100                   * Add 1 to page
       MOV  R5,@>401E                  * Set >F000 page for next file
       AI   R5,>0100                   * Add 1 to page
       SBZ  0
       B    @>D000                     * Branch to start segment

In my actual program, I use the lower 8K as data pages, which are frequently switched. I wrote two calling routines called PAGE1 and PAGE2 which set the active page to the passed value.

* Page swap routines. Page is stored in R3
* Page swap into >2000
PAGE1  DATA WS2,PAGE1A
PAGE1A LI   R0,>4004                   * Set to >2000 page
       JMP  PAGE
PAGE2  DATA WS2,PAGE2A
* Page swap into >3000
PAGE2A LI   R0,>4006                   * Set to >3000 page
PAGE   MOV  @>0006(R13),R1             * Copy page value to bottom byte
       MOVB @>0007(R13),R1             * Copy page value to top byte
       LI   R12,>1E00
       SBO  0                          * Access the mapper
       MOV  R1,*R0                     * Set the page number
       SBZ  0
       RTWP

The other part is the module switcher, which switches the high 12k page for travel, combat, and management modes.

* Module swap. Current game mode in GSTATE
SETMOD LI   R12,>1E00
       MOV  @GSTATE,R0
       JEQ  SETTRV
       CI   R0,1
       JEQ  SETMGR
       CI   R0,2
       JEQ  SETCOM
* Set start module
       LI   R1,>1616
       JMP  SETMD2
* Set combat module       
SETCOM LI   R1,>1313
       JMP  SETMD2
* Set travel module
SETTRV LI   R1,>0D0D
       JMP  SETMD2
* Set manager module
SETMGR LI   R1,>1010
SETMD2 SBO  0
       LI   R0,>401A
       MOV  R1,*R0+
       AI   R1,>0101
       MOV  R1,*R0+
       AI   R1,>0101
       MOV  R1,*R0+
       SBZ  0
       B    @>D000                     * Branch to start of module
  • Like 3
Link to comment
Share on other sites

I, too, have easy to use solution in X4th99 and TI Basic Plus (TIB+). Neither have been released as I'm concentrating on TIB+ (due to TBforth & FBforth) and I keep adding and changing things in order to support all the new hardware being developed for the TI.

  • Like 2
Link to comment
Share on other sites

 

The Geneve section of WHTECH is full of good stuff too... Cause??? ( just like most of the DSK files are under videos... )

 

I was just browsing and spotted this:

 

----------------------------------------
          AAAA   M    M   SSSSS
         A    A  MM  MM  S
         A    A  M MM M  S
         AAAAAA  M    M   SSSS
         A    A  M    M       S
         A    A  M    M       S
         A    A  M    M  SSSSS
----------------------------------------
 
       Programmer's Documentation
          Documention: 1/19/93
               Joe Delekto
 
Note: This documentation covers the 128k
      AMS system only.  After the AEMS
      is released, documentation will
      be available.
 
----------------------------------------
 
   The AMS expanded memory card is a
unique piece of hardware, in that
mapping is simple, and lends itself
well to overlay-structured programming.
 
   Because of the AMS design, no memory
manager is necessary.  The card itself
uses a 17 bit address bus (18 for 512k)
in order to access the SRAM on the
card.  The upper 4 bits of a standard
16 bit address are used to select one
of 16 mapper registers.  The remaining
12 bits from the address bus are
combined with 12 bits taken from the
mapper register, in order to give a
maximum address bus of 24 bits in
width.  This will allow for a maximum
of 16MB to be accessed.  (AEMS)
 
   On the 128k card, only the 5 least
significant bits (6 for 512k) are used
from the mapper register.  The other
pins from the mapper output are
unconnected.  "Mapping" is accomplished
by changing the value in the mapper
register, to point to one of 32 pages
(64 on 512k).  No reading/writing or
any transfer of memory is involved with
mapping.  All that is changed is the
pointer to the RAM chip on the address
bus.
 
   Because of this, mapping can be done
in a few clock cycles, using only a
couple of instructions.  Programs which
take advantage of the AMS can be
extremely large, with about no change
in execution time.  As with any memory
expansion, there are limitations on
overlay sizes.
 
   It is recommended for the AMS
system, that the root segment of your
program be placed in low memory.
(>2000 - >3FFF)  Code overlays, from 4k
to 24k in size, (in 4k increments) can
be mapped in within the upper 24k of
memory.  This means that an 8k root
segment can call as many (up to 24k)
overlays as necessary.  The result is a
HUGE program, with structure and
modularity.  (Most desired in the
programming field!)
 
   This document will describe how
mapping works, as well as the AMS
resident library created by Art Green
and myself.  I will also go into some
detail as to how you can use Charles
Earl's Hot Bug debugger to debug AMS
code.  Before I get to the meat of this
document, I would like to explain why
we chose our method of memory
expansion.
 
   We chose 4k pages for two reasons.
First, it made the hardware design
simple, and made utility routines short
and fast.  Second of all, since our
system uses overlay methods, many 4k
overlays can reside in memory at once.
The overlays can be any size from 4k to
24k in length, falling on 4k page
boundaries.  The larger the overlay is,
the less overlays you can have.  You
can have 4k, 8k, 12k, 16k, or 24k
overlays, and overlay size determines
how many overlays you can have.  (i.e.
Six 4k, three 8k, etc.)
 
   You are NOT required to use only one
size of overlay.  For instance, you
could have one 12k overlay, and an 8k
overlay, along with a 4k overlay. (24k
total).  Keep in mind that most
subroutines fall well under 4k of
space!  This means that MANY
subroutines can be placed within just
one 4k overlay!
 
   We believe you will find this to be
one of the most flexible memory
expansion systems, ever to be designed
for the TI-99/4A.  Many interesting
applications, besides large programs,
can be developed.  We are making all
information on the use of the memory
available, so that programmers can make
full use of its abilities.
 
----------------------------------------
 
PART ONE:  Map Modes and Registers
==================================
 
   Because the AMS has a 17 bit address
bus, (18 for 512k) and the TI-99/4A
only has a 16-bit address bus, the
extra bit(s) need to come from
somewhere.  These extra bits are taken
from the mapper registers.  4 bits are
taken from the memory address on the
9900 bus, and used to select one of 16
mapper registers.  The remaining 12
bits from the 9900 bus are combined
with the 5 bits from the mapper
register, to make a new bus with 17
bits.  The actual paging process is
done by changing the values in the
mapper registers, to point to new pages
in memory.  Here is the address
diagram:
 
* From 9900 Address bus:
 
>A000
 |+--
 ||
 |+---> 12 MSB to 12 LSB of new address
 |
 +----> 4 MSB to mapper register select
 
* From mapper
 
 5 bit output forms 5 MSB of new address
 
A15 - Mapper Register Select
A14 - "    " "      " "    "
A13 - "    " "      " "    "
A12 - "    " "      " "    "
 
      Mapper Address Bus
 
A11 -       MA11
A10 -       MA10
A09 -       MA09
A08 -       MA08
A07 -       MA07
A06 -       MA06
A05 -       MA05
A04 -       MA04
A03 -       MA03
A02 -       MA02
A01 -       MA01
A00 -       MA00
 
* On Mapper Address bus:
 
     +---+
A15 -| M |- MA16
A14 -| a |- MA15
A13 -| p |- MA14
A12 -| p |- MA13
     | e |- MA12
     | r |
     +---+
 
   Since only the >A000 - >F000 range
inside memory is mapped, register >A
through >F are used in the mapper.
Note the mapper register number
corresponds with the 4 MSB of the
address being accessed.
 
   Once the mapper register is loaded
with a page number, (5 bit address
which forms 5 MSB on AMS bus) any read
or write to the 4k block it corresponds
to, will access that 4k memory page.
 
   For example:  I load mapper register
10 (>A) with page number >15.  Any time
I read/write or access the >A000 block,
I will be writing to the 4k page >15.
 
   If I were to load mapper register
10 with >15, then perform a CLR @>A02E,
I will actually be writing (from the
AMS address bus) the address:
>0001502E.  Notice how the page/offset
are combined on the AMS bus to get a 17
bit address (show here as 32 bits for
clarity).
 
   It will be worthwile to note, that
even though the address was >A000, the
>A had no influence whatsoever on the
new address.  The primary purpose of
the >A was to select which mapper
register the 5 bits will come from.
The same holds true for the entire
>A000 - >FFFF range.  Because >A - >F
are used to select the mapper
registers, we have 6 registers to use,
and 6 4k boundaries.  To load
consecutive addresses, just load
the consecutive page numbers into the
mapper registers.
 
   The AMS system works in two modes.
map mode, and pass mode.  Power-up
places AMS into pass mode.  When the
mode is in pass, AMS acts as a plain
32k card, and the mapper passes actual
addresses used to the AMS bus.  (i.e. a
CLR @>A000 will clear the memory
location at >A000).  There is no
difference between pass mode, and
standard 32k mode.  The second mode is
map mode.  Map mode is the mode which
uses the MSB of an address to select
the mapper register, then dump the
register's contents to the AMS bus.
Note, when map mode is enabled, it
would be a good idea to initialize the
mapper registers to known values!
 
   The CRU address for AMS is >1E00.
In order to use mapping, some CRU
instructions are required to: 1) Switch
AMS between map/pass modes, and 2)
enable/disable register read/writes.
Below is the code which changes map
modes for AMS.
 
 LI   R12,>1E00  * AMS CRU Address
 SBO  1          * Enable Mapping
 SBZ  1          * Disable Mapping
 
   This is the only code required to
switch back between modes.  Only 2
instructions are necessary.
 
   In order to access the mapper
registers, CRU bit 0 must be set.  When
it is, DSR space is temporarily
disable, so that writing in the >4000
space will set a map register.  When
CRU bit 0 is set to zero, original DSR
space is recovered, with no side
effects.  It is recommended that you
enable mapper registers, write their
values, and then disable them
immediately.  The reason being that
forgetting to disable the registers
will keep you from accessing and DSR
routines.  To enable and disable
registers, use the following code:
 
 LI   R12,>1E00  * AMS CRU address
 SBO  0          * Enable Registers
 .
 .               * Set registers here
 .
 SBZ  0          * Disable Registers
 
 
   In order to load a mapper register
with a page number, all you need to do
is write to the >4000 block.  To
determine which mapper register you
wish to change, use the follow
calculation:
 
   MRAD = 2 * Register# + >4000
 
   So that to clear mapper register 10,
you would use:
 
      2 * >A + >4000 = >4014
 
   CLR  @>4014  * Clear Register 10
 
   Note, you can also read from a
mapper register, for the purpose of
saving previous page values:
 
SPAGE BSS  2             * Hold Page #
 
EXMPL MOV  @>4014,@SPAGE * Get MR10
      CLR  @>4014        * Clear MR10
      .
      .
      .
      MOV  @SPAGE,@>4014 * Restore Page
      RT
 
   Because writing to the mapper
registers is just writing to an
address, indirect addressing could be
used as well.  For example, Consider
setting up the mapper so that when in
map mode, addresses are the same as in
pass mode.  It is always a good idea to
first set up the mapper registers, and
then go into map mode.  While in map
mode, registers can be changed at will
to point elsewhere.  If your code to do
mapping resides in upper memory, take
care NOT to change the register where
your code is executing.  Pointing to
some other place in memory will
continue execution on the new page,
causing undesired, or unknown results.
It is possible though, to point to a
new page where code is executing,
provided that valid code exists at the
current offset and new page.
 
   Below is an example which sets up
the mapper registers as normal 32k pass
mode, yet places the mapper into map
mode.  The registers can be changed
later to access other pages of memory.
 
PAGES DATA >0A00,>0B00,>0C00
      DATA >0D00,>0E00,>0F00
 
START LI   R12,>1E00   * AMS CRU
      LI   R1,PAGES    * Page Table
      LI   R2,>4014    * Start at MR >A
      LI   R3,6        * 6 Pages to set
 
      SBO  0           * Enable MR's
RSET  MOV  *1+,*2+     * Write to MR
      DEC  R3          * Dec counter
      JNE  RSET        * Continue
 
      SBZ  0           * Disable MR's
      SBO  1           * Enable map mode
      END
 
   IMPORTANT:  Note that the 5 bit page
value is placed in the most significant
byte of the mapper register.  Because
only 5 bits are used in a map register,
and because the 2 cycle read/write on
the data bus loads the most significant
byte last, the mapper is loaded with
this value.  Therefore, page >18 would
be >1800, page >05 would be >0500, etc.
It might be worthwhile that AEMS
addresses page numbers normally, since
12 bits are used instead.
 
----------------------------------------
 
PART TWO: Overlay Techniques
============================
 
   Because the AMS system is able to
map overlays up to 24k in length, on 4k
boundaries, it lends itself well to
program development using overlays.
 
   First, a root segment is
established, which will contain the
code to call an overlay.  The root
segment must remain in memory (without
using tricky code to map it out), and
will contain the routine used to call
an overlay subprogram.
 
   We recommend the following: 1) Place
the root segment into low memory (8k).
2) Make all overlay calls BLWP
routines.  Below is the stub code for
both the root segment overlay manager,
which is used to handle the simulation
of a BLWP vector for a mapped
environment.
 
****************************************
 
* Overlay Manager
* Version 1.0
* R.A.Green
 
OVMGR SBO  0         * Enable map regs
      MOV  *R11+,R10 * Get N # pages
      MOV  *R11+,R9  * Get 1st map reg
      MOV  *R11+,R7  * Get 1st page #
OMGR2 MOV  R7,*R9+   * Set mapper reg
      AI   7,>0100   * Add 1 to page #
      DEC  R10       * Loop for N pages
      JGT  OMGR2     * Finish loop
      SBZ  0         * Disable map regs
      MOV  *R11,*R11 * Get real BLWP vec
      MOV  *R11+,R7  * Get WSP
      MOV  *11,R9    * Get sub address
      MOV  R13,@26(R7)  * Simulate BLWP
      MOV  R14,@28(R7)  *
      MOV  R15,@30(R7)  *
OMGRW EQU  $-12      * OVMGR workspace
 
      LWPI 0         * R6,R7 user wrkspc
      B    @0        * R8,R9 call sub
      BSS  2         * R10
      BSS  2         * R11
      DATA >1E00     * R12 AMS CRU addr
      BSS  6         * R13 - R15
 
****************************************
 
   Below is the code which replaces the
original BLWP call in the root segment.
This is done for every subroutine that
generates an overlay.
 
* Overlay Call
* Version 1.0
* R.A.Green
* BLWP @OSUB
 
OSUB   DATA OMGRW  * Manager WSP
       DATA $+2    *
       BL   @OVMGR * Use overlay manager
       DATA N      * # Pages in overlay
       DATA >40xx  * 1st mapper reg addr
       DATA n      * 1st page number
       DATA sub    * REAL BLWP vector
 
****************************************
 
   In order to generate a call for an
overlayed subroutine, the real BLWP
call must be replaced by the OSUB
information.  Now, the overlay
generator needs to know: 1) How many
pages the overlay is.  Remember that it
can be 4k-24k in length, broken into 4k
pages.  The routine needs to know what
mapper registers to start mapping the
'N' pages in at.  The first page number
the overlay resides on is also given,
along with the ACTUAL BLWP vector
address for the overlayed routine.
 
   To illustrate this, let us say our
root segment is in the >2000 - >3FFF
block.  We have created an overlay, and
inside the overlay is a routine called
INPUT, for which the BLWP vector starts
at >C2E0.  The overlay is in page >18
of memory, and is only 4k, or 1 page in
length.  To call the overlay, we would
use the following code:
 
   BLWP @OSUB1  * Call overlay stub
   DATA >0001   * 1 page long (4k block)
   DATA >4018   * >C000 block
   DATA >1800   * 1st page # (only one)
   DATA >C2E0   * BLWP Vector
 
 
   Note, it would be very useful to
have a program loader to load segments
of code into different pages.  Although
such a loader exists for AMS, it is
only used for AMS files with special
headers for overlay and root segments.
A similair loader can be constructed,
which loads the overlays into their
corresponding pages.
 
   The overlay code examples above, are
the code segments installed
(automatically) by the linker.  That
eliminates the need for passing the
arguments to the overlay generator, and
keeping track of relative page
addresses.
 
   You may however, choose your own
method of overlaying.  We made it very
flexible to customize your software so
you can choose how you want to map.
Keep in mind that other programs may be
resident to AMS, and using the
linker/loader will ensure that AMS
programs are page relocatable, and
won't overwrite memory resident code.
 
----------------------------------------
 
PART THREE: Using Hot Bug with AMS
==================================
 
   Most often overlay programs are
tedious to debug.  If you have access
to Charles Earl's Hot Bug debugger, I
recommend you learn how to use it.  It
is by far one of the best debugging
utilities available, and can certainly
work well for debugging AMS programs.
 
   Since Hot Bug will also load program
files, you can use the debugger to
change the page map, and load in your
overlay code!
 
   Hot Bug Command Summary
   ER  - Edit Register
   EW  - Edit Word
   DM  - Display Memory
   SPC - Set Program Counter
   G   - Go (# of steps)
 
 
   In order to access the registers,
and check the code/data within pages,
we need to enable both the mapper, and
the registers.  Choose a word of memory
that does not have code to use the
following commands: (For this example,
I use >3FF0)
 
   1:  ER 12 1E00
   2:  EW 3FF0 1D00
   3:  EW 3FF2 1D01
   4:  SPC 3FF0
   5:  G 2
 
(1: Load Register 12 with CRU >1E00)
(2: Put SBO 0 at 3FF0 Enable REGS  )
(3: Put SBO 1 at 3FF2 MAP Mode     )
(4: Set program counter to >3FF0   )
(5: Execute 2 instructions         )
 
   NOTE: If the mapper is in an unknown
state, (register values unknown), you
will want to set the registers before
actually placing into map mode.  Just
use G 1 instead, edit the registers
(see below) and then G 1 again to get
into map mode.
 
   To read/write to the mapper
registers, use the DM command to look
at the >4000 block.  Only addresses
>4000 - >4020 are of interest to us.
(Mapper regs 0 - 16).
 
   NOTE: Even though the upper 24k is
mapped using mapper registers 10 - 16,
the other mapper registers can be used
for temporary storage.
 
   1: DM 4000
 
(1: Display Memory at >4000)
 
   In order to change a register value,
just use the EW command.  For example,
to load mapper register 11, (>B000
block) with page >15, use the
following:
 
   1: EW 4016 1500
 
(1: Load mapper register 11 with >15)
 
   Now let's try an experiment.  What
we will do is write the same page to 2
different mapper registers, and observe
what happens.  Use the following
commands:
 
   1: EW 4014 1500
   2: EW 4016 1500
   3: DM A000
 
(1: Load mapper register 10 with >15)
(2: Load mapper register 11 with >15)
(3: Display memory at >A000         )
 
   Note what the data in memory is at
>A000.  Now, if you use DM B000, you
should see the same data you saw
before.  Let's try something
interesting.  Use the following
commands:
 
   1: EW A000 FACE
   2: DM B000
 
(1: Put value >FACE into >A000)
(2: Display memory at >B000   )
 
   When you use the DM B000, you should
get a surprise.  When you wrote to
A000, you actually changed the word at
B000, as well as A000.  Why?  Because
both 4k block point to the same page!
Perhaps now you can envision some of
the interesting tricks you can
accomplish with the AMS system.  One
such application is the arbitrary
locating of data buffers!
 
   It is also possible to load a memory
image file, on non-consecutive pages,
and yet still load the mapper registers
such that the program is contiguous in
the upper 24k!  If that's so, then it
means we can load E/A option 5 program
files anywhere inside AMS, and then
just map in their pages to the proper
blocks in high memory!  In this manner,
even code with absolute origins becomes
page relocatable, at least for paging
purposes.
 
   By placing page numbers into the
registers, and using Hot Bug's LOAD
command, you can load overlay image
files.  Keep track of the address for
the BLWP vector in the overlay, as well
as the page you LOAD it into, and how
many pages it takes up.  This
information you will need to pass to
the overlay generator in your program.
 
   See?  Loading, debugging, and
running overlay code on the AMS system
is very feasible, and not difficult.
 
----------------------------------------
 
   This concludes this section of
programmer's documentation.  The next
document will focus on the memory
resident utility routines, which AMS
programmers can use in their software.
Memory allocation, exit code, memory
moves, and far VDP read/write routines
are available.  Also, AMS program have
access to the E/A 5, and AMS Overlay
program file loader.  The loader will
load either type of file.  The exit
routines for AMS have the option of
keeping the programmer's code resident
for instant execution when desired.
 
   We have worked very hard for the
past couple of years, to make this
memory expansion as user friendly as
possible.  We are, and will continue to
supply support for the AMS card.
Without the software support to use
AMS, it would just be an expensive
paperweight.
 
                     <<< Joe Delekto >>>

Bingo. Thanks.

  • Like 1
Link to comment
Share on other sites

So if someone could verify this.

 

The SAMS card has 8 registers mapped starting @ $4000 and looks like this.

 

Reg : 9900 memory map

-------- : -------------------

4000 : 2000-2fff

4002 : 3000-3fff

 

4004 : a000-afff

4006 : b000-bfff

 

4008 : c000-cfff

400a : d000-dfff

 

400c : e000-efff

400e : f000-ffff

 

And for the kicker...

 

All you have to do is write any value from 0-255 to a register and that pages in that particular 4k segment of SAMS RAM into the 9900 map ?

Edited by marc.hull
Link to comment
Share on other sites

Mostly correct! Except that in SAMS you actually map the entire address, not just the RAM sections. So it's like this:

 

4000: 0000-0FFF

4002: 1000-1FFF

4004: 2000-2FFF

4006: 3000-3FFF

4008: 4000-4FFF

400A: 5000-5FFF

400C: 6000-6FFF

400E: 7000-7FFF

4010: 8000-8FFF

4012: 9000-9FFF

4014: A000-AFFF

4016: B000-BFFF

4018: C000-CFFF

401A: D000-DFFF

401C: E000-EFFF

401F: F000-FFFF

 

Obviously mapping to ROM areas has no effect in this instance.

  • Like 1
Link to comment
Share on other sites

Mostly correct! Except that in SAMS you actually map the entire address, not just the RAM sections. So it's like this:

 

4000: 0000-0FFF

4002: 1000-1FFF

4004: 2000-2FFF

4006: 3000-3FFF

4008: 4000-4FFF

400A: 5000-5FFF

400C: 6000-6FFF

400E: 7000-7FFF

4010: 8000-8FFF

4012: 9000-9FFF

4014: A000-AFFF

4016: B000-BFFF

4018: C000-CFFF

401A: D000-DFFF

401C: E000-EFFF

401F: F000-FFFF

 

Obviously mapping to ROM areas has no effect in this instance.

After Asgard started selling the AMS I asked then why not in the future we could not bateryback the card and make use of :

 

4000: 0000-0FFF

4002: 1000-1FFF

400C: 6000-6FFF

400E: 7000-7FFF

As this would really improve the TI types of software and would allow for a true DOS OS.

Link to comment
Share on other sites

Mostly correct! Except that in SAMS you actually map the entire address, not just the RAM sections. So it's like this:

 

4000: 0000-0FFF

4002: 1000-1FFF

4004: 2000-2FFF

4006: 3000-3FFF

4008: 4000-4FFF

400A: 5000-5FFF

400C: 6000-6FFF

400E: 7000-7FFF

4010: 8000-8FFF

4012: 9000-9FFF

4014: A000-AFFF

4016: B000-BFFF

4018: C000-CFFF

401A: D000-DFFF

401C: E000-EFFF

401F: F000-FFFF

 

Obviously mapping to ROM areas has no effect in this instance.

That's very good info and thank you.

 

 

Makes the card a bit dangerous if you can duplicate memory locations with different contents. I wonder why the design included the option of paging over the ROM and $8000 areas.

 

Anyway this post will give me the confidence to at least think I'm on the right track.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...