Após alguns meses do lançamento da última versão do PopolonY2k Framework, venho trabalhando em alguns programas de exemplo para reforçar os conceitos implementados no framework bem como na difusão de conhecimento de tudo o que tem nele desenvolvido.
A comunidade nacional tem utilizado muitas partes do framework e principalmente a comunidade internacional tem reportado alguns problemas encontrados, principalmente no Pop!Art, o que me levou a verificar que de fato os problemas estão relacionados a algumas peças do framework e que brevemente serão ajustadas ainda esse ano.
No grupo de WhatsApp “MSX Pascal, C, ASM e etc” temos discutido muito sobre assuntos relacionados a desenvolvimento para retro-machines, principalmente para o MSX e em algumas dessas discussões surgiram alguns questionamentos sobre o uso das rotinas de uso da mapper do PopolonY2k Framework.
Por esse motivo, preparei um sample com um código bem elucidativo sobre o uso das principais rotinas de alocação e paginação da mapper que fazem uso das rotinas do MSXDOS2 disponíveis no framework, conforme análise e explicação pode ser vista no vídeo abaixo.
O framework também contempla uso da mapper através do acesso direto a portas de I/O, o que torna o seu uso mais rápido, entretanto essas rotinas são as chamadas “mal comportadas” e devem ser utilizadas com muita cautela principalmente quando se tem completa certeza de que não há conflitos com áreas pré-alocadas pelo próprio MSXDOS2 via suas rotinas padrão.
O Pop!Art faz isso com extrema segurança, onde o mesmo aloca e preenche os dados da música VGM usando as rotinas de mapper padrão do MSXDOS2 e usa as de acesso direto, apenas no momento em que está tocando quando está apenas fazendo paginação dos segmentos da mapper pré-alocados pelas rotinas do MSXDOS2.
Os exemplos estão sendo adicionados diretamente no repositório do projeto na OldSkoolTech no SourceForge.net e podem ser consultados nesse link aqui.
O sample de uso da mapper (maprtest.pas) está completamente comentado sendo bem auto-explicativo, além de ter uma análise bem completa no vídeo acima, podendo seu código ser conferido abaixo:
(*<maprtest.pas>
* Memory mapper routines test.
*
* - Routines tested:
* - InitMapper;
* - GetMapperPageByAddress;
* - PutMapperPageByAddress;
* - AllocMapperSegment;
* - FreeMapperSegment;
*
* CopyLeft (c) since 1995 by PopolonY2k.
*)
(**
*
* $Id$
* $Author$
* $Date$
* $Revision$
* $HeadURL$
*)
{-----------------------------------------------------------------------------}
{ PopolonY2k Framework dependencies }
{-----------------------------------------------------------------------------}
{$i types.pas}
{$i helpchar.pas}
{$i msxbios.pas}
{$i extbio.pas}
{$i maprbase.pas}
{$i maprallc.pas}
{$i maprpage.pas}
{-----------------------------------------------------------------------------}
{ Module definitions }
{-----------------------------------------------------------------------------}
Type TMappedBuffer = Array[0..ctMaxMapperPageSize] Of Char; { Mapped 16K bufr }
{-----------------------------------------------------------------------------}
{ Helper functions }
{-----------------------------------------------------------------------------}
(**
* Print part of a buffer content passed as reference.
* @param aBuffer Reference to buffer to be printed;
*)
Procedure PrintBuffer( Var aBuffer : TMappedBuffer );
Const
__ctMaxCol : Byte = 10;
Var
x, y : Byte;
Begin
For y := 0 To __ctMaxCol Do
For x := 0 To __ctMaxCol Do
Begin
GotoXY( ( x + 1 ), ( y + 1 ) );
Write( aBuffer[( x + y ) * __ctMaxCol] );
End;
WriteLn;
WriteLn( 'Press <enter> to continue' );
ReadLn;
ClrScr;
End;
{-----------------------------------------------------------------------------}
{ Main program variables and constants }
{-----------------------------------------------------------------------------}
Const
ctDefaultPage = $8000; { Default page for data - Page 2 }
Var
maprHandle : TMapperHandle;
chKey : Char;
nActiveSegmentId : Byte;
aSegments : Array[0..1] Of Byte;
aDataBuffer : TMappedBuffer Absolute ctDefaultPage;
{-----------------------------------------------------------------------------}
{ Main program entry point }
{-----------------------------------------------------------------------------}
Begin { Main program entry }
ClrScr;
{ Initialize mapper system }
If( Not InitMapper( maprHandle ) ) Then
Begin
WriteLn( 'Error to initialize Mapper' );
Exit;
End;
WriteLn( 'Mapper succesfully initialized' );
{ Get the current segment used by data buffer on stack }
aSegments[0] := GetMapperPageByAddress( maprHandle, Addr( aDataBuffer ) );
WriteLn( 'Main Segment -> ', aSegments[0], ' on page 2' );
WriteLn( 'Press <enter> to see it''s content' );
ReadLn;
ClrScr;
{ Fill page 2 on Main Segment, fully with X character }
FillChar( aDataBuffer, SizeOf( aDataBuffer ), 'X' );
PrintBuffer( aDataBuffer );
{ Alloc new segment to put data (MSXDOS2 BIOS) }
If( Not AllocMapperSegment( maprHandle,
maprHandle.nPriMapperSlot,
UserSegment, aSegments[1] ) ) Then
Begin
WriteLn( 'Mapper segment allocation failed' );
Exit;
End;
WriteLn( 'New Segment -> ', aSegments[1], ' successfully allocated' );
WriteLn( 'Type <enter> to see it''s content' );
ReadLn;
ClrScr;
{ Activate page 2 content to the New Segment allocated }
PutMapperPageByAddress( maprHandle, aSegments[1], Addr( aDataBuffer ) );
{ Fill page 2 on New Segment fully with Y character }
FillChar( aDataBuffer, SizeOf( aDataBuffer ), 'Y' );
PrintBuffer( aDataBuffer );
nActiveSegmentId := aSegments[1];
{ Handle user switch segment contents }
Repeat
GotoXY( 1, 19 );
WriteLn( 'Active Segment -> ', nActiveSegmentId );
WriteLn;
WriteLn( '0 - Switch to Main Segment ', aSegments[0] );
WriteLn( '1 - Switch to New Segment ', aSegments[1] );
WriteLn( 'ESC - exit' );
chKey := ReadKey;
If( chKey In ['0', '1'] ) Then
Begin
nActiveSegmentId := aSegments[Byte( chKey ) - Byte( '0' )];
{ Activate page 2 content to segment chosen by user }
PutMapperPageByAddress( maprHandle,
nActiveSegmentId,
Addr( aDataBuffer ) );
{ Show segment content }
PrintBuffer( aDataBuffer );
End;
Until( chKey = #27 );
WriteLn;
{ Release all segments allocated by the application }
If( Not FreeMapperSegment( maprHandle,
maprHandle.nPriMapperSlot,
aSegments[1] ) ) Then
Begin
WriteLn( 'SegmentId -> ', aSegments[1], ' deallocation failed' );
Exit;
End;
WriteLn( 'All segments successfully deallocated' );
End.
Em tempo, Ricardo Jurczyk Pinheiro desenvolveu um excelente e mais completo exemplo que faz uso das rotinas de mapper do framework de maneira mais completa, explorando inclusive outras funções de suporte a mapper do framework.
Seu exemplo pode ser visto no link abaixo:
https://github.com/ricardojpinheiro/test/blob/master/mappdemo.pas
Enjoy coding 🙂
Referências