Home > CBM > C128 > Media Player 128 > Modules
0 Subpages:

This page describes the internal design and operation of Media Player 128. See the main page for general information and downloads. MP128 combines several technologies and software modules to provide a conherent user experience. This page describes those modules, some of which you may find useful in your own projects.

  Contents  
  Modules Overview  
  • MP128.BIN is the core of Media Player 128: it's an assembly-language program that plays one audio/video file.
  • MP128GUI.BAS provides the main user interface: it allows file selection and playlist editing; it calls MP128.BIN for each file in a playlist.
  • MP128INI.BAS boots the system: it loads the various modules and launches MP128GUI.BAS
  • JLOAD128.BIN allows quicker loading from JiffyDOS-compatible devices; it is the first module loaded by MP128INI.BAS. See JiffySoft128 for details about this technology.
  • MJ-DRIVE.BIN is the mouse/joystick input driver
  • ARROW80.BIN creates a simple software sprite for the 80-column (VDC) screen
  • FILEHELP.BIN provides ASCII to PETSCII conversion (for .M3U files)

In other words, these modules work together to support MP128.BIN.

  MP128.BIN  

This file is a collection of codecs along with the actual file player. A separate page will discuss the player in detail. Currently there are 4 different audio codecs:

  1. Signed 8-bit audio (.AIF files)
  2. Unsigned 8-bit audio (.WAV, .DFF files)
  3. Unsigned 4-bit audio (.CIF files)
  4. Compressed 4-bit audio (2-bit .CIF files)

Each audio codec has two implementations: one for fast-serial devices and another for REU-based playback. This gives us 8 audio codec implementations. Most of these implementations also have separate versions for 1-MHz or 2-MHz CPU speed. The two 8-bit codecs, however, require 2-MHz operation. Consequently, there are 12 distinct audio codec implementations!

There are also two video codecs. One performs actual video decoding while the other simply renders a graph of the audio buffer during audio-only playback. Thankfully, each video codec has just a single implementation, regardless of storage device or CPU speed.

Another key component of MP128.BIN is the demultiplexer (demux). It primarily routes data from a file to a buffer for either audio or video. However, any unrecognized data packet (for example, caption text or copyright info) will be routed to a junk buffer.

  MP128GUI.BAS  

The main user interface is an uncompiled BASIC program. You can LIST it to see the details of how it works. Basically it draws out one of several screens (depending on function/mode in use) and then waits for a key press, a mouse click, or a joystick fire. If a mouse click or joystick fire is detected, the pointer position is checked against a list of "hit boxes". If a hit-box is identified, the corresponding keyboard command is executed.

MP128GUI maintains three string arrays. One is for the current directory, another is for the current playlist, and third is for file paths. On the screens which show a directory or playlist, there is also a file selector. This is another (text) pointer in addition to the mouse pointer. On the right-side of these screens is a scroll bar which indicates how many of the listed files are shown compared to the total number of files. Clicking the top or bottom arrow of the scroll bar will advance/retreat the list of shown files.

When the user clicks Play, MP128GUI will go through each file in the playlist and do a few things for each:

  1. Set up the correct path for the file
  2. Open the file and call a 'check' function in MP128.BIN; this will display codec info if the file is recognized.
  3. If the file is recognized and an REU is needed, the file will be buffered.
  4. If the file is recognized, the 'play' function in MP128.BIN will be called.
  5. The file is closed.

If the file is not recognized (or an REU is needed but not available), then MP128GUI will proceed to the next file in the playlist (if any). After playing all files in the playlist, MP128GUI will return to the directory listing screen.

  MP128INI.BAS  

To reduce the size of MP128GUI, a separate program, MP128INI is used to initialize some variables and load a few ML modules before launching MP128GUI. The first thing it does is set the start-of-variables to $D000 (in Bank 1). Next it de-allocates any VIC-II bitmap from bank 0 (it will not be needed by either the MP128GUI or MP128.BIN) and saves 9K of RAM.

Then MP128INI defines some of the most frequently used variables (with dummy values). This will make accessing them faster in uncompiled BASIC. Next it determines the current video mode (40 or 80 column) and sets up some variables for display based on that.

Then it sets up variables for 3 modules:

  1. Mouse/Joystick Driver
  2. 80-Column Mouse
  3. Hit-test code and data

Note the 80-Column mouse and Hit-test code are in the same executable (ARROW80.BIN). Although variables are set up now, the actual modules aren't loaded yet. One interesting detail is the use of POKE 828,183. This disables expansion of function keys into strings and makes the RAM at $1000-10FF available for use. That RAM is used to hold a list of 'hit-boxes' (clickable regions). It is initialized with 'hit-boxes' for the file-list (either directory or playlist), and 'hit-boxes' for the scroll bar.

After initializing some more variables (scalars to control arrays), it checks that the last-used device number is an IEC-serial type (device greater than or equal to 4). If not (you loaded from cassette?), it will prompt the user for a device number (with 8 as the default). Then it initializes unit numbers, partition numbers, and paths for the file-list and system files. It also initializes variables for play lists and 1581-style paths.

Next it checks if the mouse/joystick driver is installed. If so, it assumes the modules have already been loaded and skips ahead. If not, it begins loading modules. The first module loaded is JLOAD128 to speed up loading if the user has no fast-serial device but does have a JiffyDOS compatible device. It sends a command to channel 15 of the device to see if fast-serial is supported. If not, it initializes JLOAD128 and checks to see if JiffyDOS is supported. In any event, a message is printed to indicate which type of loading is being used; if neither fast-serial nor JiffyDOS is supported then it says KERNAL which means slow-serial.

Next it loads the modules MJ-DRIVE.BIN, ARROW80.BIN, FILEHELP.BIN, and finally the main binary MP128.BIN (not really a module). With all modules loaded, it ends by launching the main user interface, MP128GUI.BAS.

  MJ-DRIVE.BIN  

The main mouse-joystick driver loads into $E00-FFF. The first 64 bytes define the 40-column mouse/joystick pointer's image (a VIC-II sprite). The shape is not critical; feel free to redefine it.

The driver is installed and activated by calling $E40 (SYS 3648); this turns on BASIC Sprite 1 (assembly language sprite 0). The driver is removed by calling $E43 (SYS 3651); this turns off Sprite 1. In either case, I/O registers should be enabled (BANK 15 for example) before calling a routine. The source code is designed for a simple change if you want to use a different sprite number.

Installing the driver redirects the system IRQ. It works in close partnership with both the KERNAL and BASIC IRQ routines. Although Media Player 128 doesn't use BASIC's split-screen mode, the driver is designed to work in this mode. This means it must detect when a split-screen is present, and abort normal IRQ processing for one of the two phases used by the system IRQ.

You can define most of the pointer's image properties (color, X/Y-expansion, etc.) by writing to VIC-II registers or using the BASIC Sprite command. However, the mouse pointer can not be moved programmatically by writing to the VIC-II sprite registers ($D000-D010). This is because the BASIC IRQ is responsible for positioning the sprite. To programmatically relocate the sprite, either use the BASIC Movspr command or write to BASIC's shadow registers ($11D6-11E6) of the VIC-II. Note there is a simple switch in the source code you can change if you want to use the driver without the BASIC IRQ.

Connect a mouse to game port #1 and/or a joystick to game port #2. Because button presses or joystick movement in either game port can interfere with the keyboard, the game works with the KERNAL IRQ such that a scan of the keyboard only occurs when neither game port is active. Similarly, keyboard presses might be interpreted as mouse clicks without this "de-interference" code. A "positive logic" reading of the "de-interfered" mouse/joystick reading is available by reading $E46 [PEEK(3654)]:

ValueMeaning
0Nothing pressed
1Joystick up or mouse right-click
2Joystick down
4Joystick left
8Joystick right
16Joystick fire or mouse left-click

The joystick handling is pretty simple (it could be coded in BASIC), but the mouse driver is more complex. First of all, it is auto-ranging. You see the SID chip (which reads mouse position changes) can report a 'pot' value from 0 to 255, however a real mouse will use a smaller range (like 2 to 220 for example) which varies with different hardware. So the driver records the minimum and maximum values (separately for the X and Y axes) and calculates the difference which I call the span. The correct span must be used for the mouse to move smoothly.

Second of all, there is something called digitization noise. This basically means a value read from SID will tend to fluctuate by ± 1 even when the mouse is stationary. Uncorrected, the pointer image would seem to tremble when not being moved. Some mouse drivers work around this issue by averaging two values together to calculate a position. However, such drivers move the pointer at half-speed; the mouse feels stiff and less responsive. MJ-DRIVE uses a different technology which allows for full speed movement. Instead of averaging values, it simply ignores any delta value of plus or minus 1. This makes the mouse feel very responsive, but one drawback is that it is impossible to move the pointer by only one pixel. If you really needed to move exactly one pixel, you would need to move away by 3 pixels then back by 2.

By default, the mouse pointer is confined to the visible portion (inside borders) of the 40-column screen. See the source code for values you can write to if you want to confine the pointer to a different region (like a GUI window, although this feature is not used by Media Player 128).

To determine the current pointer position, either read the VIC-II sprite registers or use BASIC's RSPPOS() function. An (X,Y) value of (24,50) represents the top-left of the screen, while a value of (343,249) represents the bottom-right of the screen.

  ARROW80.BIN  

ARROW80 controls a software sprite (mouse pointer) on the 80-column text screen. It does not directly read the joystick or mouse hardware. Instead it mirrors the position of Sprite 1 of the VIC-II (even if the sprite is disabled or the 40-column screen is unused). That is, in Media Player 128, it works in conjunction with MJ-DRIVE. You could use this without MJ-DRIVE in your own projects, but then you would need to manually position BASIC Sprite 1. If you do use both in your own project, it is important that MJ-DRIVE is installed first. This is because this driver also intercepts the system IRQ (and MJ-DRIVE can't be started with a custom IRQ).

After loading ARROW80, call $C00 (SYS 3072) to enable the driver. Call $C03 (SYS 3075) to remove the driver. In either case, I/O registers should be enabled (BANK 15 for example).

After enabling the driver, the mouse pointer will not be visible at first. Call $C09 (SYS 3081) to make it visible. Call $C0C (SYS 3084) to hide it. Again, I/O registers must be enabled before calling either routine. For speed (and size) the driver does not monitor access to the VDC such as printing to the screen. So it is important to hide the mouse pointer before doing screen I/O (e.g., PRINT or INPUT), and then show it again when done.

Besides reading the VIC-II's sprite position to determine a screen pixel coordinate of the pointer, you can read a text coordinate for the 80-column pointer via $D65 [PEEK(3429)] for X and $D69 [PEEK(3433)] for Y. The values range from 0 to 79 for X and 0 to 24 for Y. If you are using the 40-column display, divide the X value by 2 (ignore any fraction) to get a valid character coordinate.

  Hit Test  

The ARROW80.BIN file also contains a module called Hit Test. Its source code can be found in ARROW80/hitTest.src). Unlike the visible nature of MJ-DRIVE and ARROW80, this one is invisible to the user but still critical to the operation of MP128GUI. It manages an array of "hot boxes" (clickable regions).

The most-often used function is at $D86 (SYS 3462). This should be called when a mouse left-click or joystick fire is detected. It scans the list of hot spots based on the current mouse-pointer position for a match and returns the associated key code. It returns 0 if no match is found.

New hot spots are created simply by appending a structure to the end of the hit-test list. The current number of hot spots in the list is stored at $1000 (4096), and the first hit-test structure goes into $1001-1006. Each hit-test entry is defined as:

OffsetValue/Meaning
0Flags: 0=normal, 128=disabled
1Associated key code (PETSCII value)
2Top (Y min)
3Left (X min)
4Bottom (Y max+1)
5Right (X max+1)

So if you wanted a region with text coordinate (4,0) to (7,0) to return the code 'p' when clicked, you would append the following structure to the list:

ValueMeaning
0normal (clickable)
80PETSCII value of 'p'
0Top (Y min)
4Left (X min)
1Bottom (Y max+1)
8Right (X max+1)

Of course the user interface should draw something in that location (like the word 'Play') to indicate that it is a clickable region.

Removing an item from the list involves deleting an entry and moving all following entries up to fill in the blank. This would be slow in uncompiled BASIC, so there is a routine at $D80 (SYS 3456) to do it quickly in machine language.

  FILEHELP.BIN  

The last module has nothing to do with the GUI. It simply provides a few routines to make processing file data from BASIC faster. Two of these features are ASCII to PETSCII conversion and vice versa. You see, M3U files (playlist files that I did not make up) use ASCII encoding of filenames. Naturally the C128 and associated disk drives use PETSCII. The function at $803 (SYS 2051) will read ASCII text from a file until a line terminator is detected and store the value in a BASIC string as PETSCII. The routine at $80C (SYS 2060) will write a BASIC string in PETSCII to a file after conversion to ASCII.

There are also routines to read an unsigned integer into a variable, and to skip bytes in a file. See the source code if you are interested in those.

  Memory Map: MP128GUI  

The memory map of MP128GUI uses much of RAM Bank 0 and some (12K) of Bank 1. A significant portion of both banks is reserved for use by MP128.BIN. There are two maps for MP128GUI since the program uses memory in both RAM banks.

Bank 0
Hexadecimal
Range
Description
$00 - $FFZero page for BASIC, KERNAL, and MP128
$100 - $13DBASIC
$13E - $1FFCPU Stack
$200 - $2A1KERNAL input buffer
$2A2 - $2FFKERNAL cross-bank routines
$300 - $349KERNAL and BASIC vectors
$34A - $37FKERNAL tables
$380 - $39EBASIC CHRGET
$39F - $3D1BASIC cross-bank routines
$3D2 - $3E3BASIC Common RAM data
$3E4 - $3EFunused
$3F0 - $3FCREU dispatch code
$3FD - $3FFunused
$400 - $7E740-Column text screen
$7E8 - $7F7unused
$7F8 - $7FF40-Column sprite pointers
$800 - $94BFileHelp.bin
$94C - $9FFBASIC stack
$A00 - $AFFKERNAL and Monitor data
$B00 - $BBFCassette buffer (unused)
$BC0 - $BFFKERNAL Boot buffer (unused)
$C00 - $D7FArrow80 code
$D80 - $DFFHit-test code
$E00 - $E3F40-Column mouse pointer
$E40 - $FFFMouse/Joystick driver
$1000 - $10FFHit-test data
$1100 - $12FFBASIC data
$1300 - $167Funused
$1680 - $16FFJ-Load128 initialization
$1700 - $1BFFJ-Load128 resident code
$1C00 - $63FFMP128GUI
$6400 - $A7FFMP128.BIN
$A800 - $CFFFunused
$D000 - $DFFFI/O Registers (in particular, Color RAM at $D800-DBFF)
$E000 - $EFFFAudio buffer (4K)
$F000 - $F0FFJunk buffer
$F100 - $F2FFPlayback initialization
$F300 - $F4BFPlayback control
$F4C0 - $F4FFunused
$F500 - $F87FVideo codec
$F880 - $F8FFunused
$F900 - $FDBFAudio codec
$FDC0 - $FDFFFrame queue
$FE00 - $FEFFDemux code
$FF00 - $FF04Static MMU registers
$FF05 - $FF44KERNAL IRQ/NMI/RES dispatch
$FF45 - $FF4Funused
$FF50 - $FFCFDemux data
$FFD0 - $FFEF8502/Z80 switch code
$FFF0 - $FFF9unused
$FFFA - $FFFFCPU vectors

Bank 1
Hexadecimal
Range
Description
$00 - $FFZero page for BASIC, KERNAL, and MP128
$100 - $13DBASIC
$13E - $1FFCPU Stack
$200 - $2A1KERNAL input buffer
$2A2 - $2FFKERNAL cross-bank routines
$300 - $349KERNAL and BASIC vectors
$34A - $37FKERNAL tables
$380 - $39EBASIC CHRGET
$39F - $3D1BASIC cross-bank routines
$3D2 - $3E3BASIC Common RAM data
$3E4 - $3EFunused
$3F0 - $3FCREU dispatch code
$3FD - $3FFunused
$400 - $CFFFReserved for MP128.BIN playback
$D000 - $FEFFBASIC variables
$FF00 - $FF04Static MMU registers
$FF05 - $FF44KERNAL IRQ/NMI/RES dispatch
$FF45 - $FF4Funused
$FF50 - $FFCFDemux data
$FFD0 - $FFEF8502/Z80 switch code
$FFF0 - $FFF9unused
$FFFA - $FFFFCPU vectors

  Memory Map: MP128.BIN  

Setup routines are called in Bank 0 (see memory map above) which (among other things) copies the needed codecs into high RAM ($E000-FFFF) which will be Common RAM when playback starts. Unlike the GUI, the playback engine operates primarily in Bank 1, where video frames and decoding buffers are allocated (see map below).

The number of frames buffered depends on the image size and the video mode (bitmap or text). The minimum is 3 frames for full-size bitmap. For slightly reduced bitmap, (like 16:9 aspect ratio) there will be 5 frames buffered for bitmap mode. For text mode with custom font, there will be 16 frames regardless of image size.

Bank 1
Hexadecimal
Range
Description
$00 - $FFZero page for BASIC, KERNAL, and MP128
$100 - $1FFCPU Stack
$200 - $CCFFVideo frames buffer
$CD00 - $CEFFVideo decode buffers (2)
$CF00 - $CFFFunused (planned move of Junk buffer)
$D000 - $DFFFI/O Registers (in particular, Color RAM at $D800-DBFF)
$E000 - $EFFFAudio buffer (4K)
$F000 - $F0FFJunk buffer
$F100 - $F2FFPlayback initialization
$F300 - $F4BFPlayback control
$F4C0 - $F4FFunused
$F500 - $F87FVideo codec
$F880 - $F8FFunused
$F900 - $FDBFAudio codec
$FDC0 - $FDFFFrame queue
$FE00 - $FEFFDemux code
$FF00 - $FF04Static MMU registers
$FF05 - $FF44KERNAL IRQ/NMI/RES dispatch
$FF45 - $FF4Funused
$FF50 - $FFCFDemux data
$FFD0 - $FFEF8502/Z80 switch code
$FFF0 - $FFF9unused
$FFFA - $FFFFCPU vectors

© hydrophilic.net, 2026