From 3465042dc2a80f4090d6cf6b65ddc33ee4e48b55 Mon Sep 17 00:00:00 2001
From: Clyne Sullivan <clyne@bitgloo.com>
Date: Sun, 12 Jan 2025 15:50:21 -0500
Subject: initial schip implementation; add command line arguments

---
 src/ada_chip.adb | 132 ++++++++++++++++++++++++++++++++++++++++++++++---------
 src/cpu.ads      |  19 ++++++++
 src/isa.ads      |  11 +++--
 src/video.adb    |  41 +++++++++++++++--
 src/video.ads    |  26 +++++------
 5 files changed, 188 insertions(+), 41 deletions(-)

diff --git a/src/ada_chip.adb b/src/ada_chip.adb
index da48b7b..b6a7611 100644
--- a/src/ada_chip.adb
+++ b/src/ada_chip.adb
@@ -17,9 +17,13 @@ procedure Ada_Chip is
    State            : CPU.Instance;
    Delay_Timer      : Natural := 0;
    Sound_Timer      : Natural := 0;
+   Model            : Video.Model := Video.Chip_8;
+   Steps_Per_Frame  : Natural := 8;
 
    procedure Draw_Sprite (VX, VY : ISA.Register_Index; N : ISA.Byte) is
       use ISA;
+      use Sf;
+      use Video;
 
       X          : constant Byte := State.Registers (VX);
       Y          : constant Byte := State.Registers (VY);
@@ -27,22 +31,54 @@ procedure Ada_Chip is
       Row_Pixels : Pixel with Address => Row'Address;
       VF         : Byte := 0;
    begin
-      for I in 0 .. N - 1 loop
-         Row := State.Memory (State.Address_Register + Address (I));
-
-         for J in 0 .. 7 loop
-            if Row_Pixels (7 - J) then
-               if Video.Toggle_Pixel
-                  (Sf.sfUint32 ((X + Byte (J)) mod Video.Width),
-                     Sf.sfUint32 ((Y + I) mod Video.Height))
-               then
-                  VF := 1;
+      if Model = Super_Chip_10 and then N = 0 then
+         for I in 0 .. Byte (15) loop
+            Row := State.Memory (State.Address_Register + Address (I * 2));
+
+            for J in 0 .. 7 loop
+               if Row_Pixels (7 - J) then
+                  if Video.Toggle_Pixel
+                     (sfUint32 (X + Byte (J)) mod Video.Width,
+                        sfUint32 (Y + I) mod Video.Height)
+                  then
+                     VF := 1;
+                  end if;
                end if;
-            end if;
+            end loop;
+
+            Row := State.Memory (State.Address_Register + Address (I * 2 + 1));
+
+            for J in 0 .. 7 loop
+               if Row_Pixels (7 - J) then
+                  if Video.Toggle_Pixel
+                     (sfUint32 (8 + X + Byte (J)) mod Video.Width,
+                        sfUint32 (Y + I) mod Video.Height)
+                  then
+                     VF := 1;
+                  end if;
+               end if;
+            end loop;
+         end loop;
+
+         State.Registers (15) := VF;
+      else
+         for I in 0 .. N - 1 loop
+            Row := State.Memory (State.Address_Register + Address (I));
+
+            for J in 0 .. 7 loop
+               if Row_Pixels (7 - J) then
+                  if Video.Toggle_Pixel
+                     (sfUint32 (X + Byte (J)) mod Video.Width,
+                        sfUint32 (Y + I) mod Video.Height)
+                  then
+                     VF := 1;
+                  end if;
+               end if;
+            end loop;
          end loop;
-      end loop;
 
-      State.Registers (15) := VF;
+         State.Registers (15) := VF;
+      end if;
    end Draw_Sprite;
 
    procedure Run_Flow (ins : ISA.Opcode) is
@@ -50,6 +86,8 @@ procedure Ada_Chip is
       case ins.Value is
          when ISA.Clear_Screen => Video.Clear_Screen;
          when ISA.Ret => CPU.Ret (State);
+         when ISA.Low_Res => Video.Low_Res;
+         when ISA.High_Res => Video.High_Res;
          when others =>
             Ada.Text_IO.Put_Line ("Machine code calls are unsupported!");
             delay 1.0;
@@ -85,10 +123,35 @@ procedure Ada_Chip is
          when Set_Sound => Sound_Timer := Natural (State.Registers (X));
          when Reg_Store => CPU.Reg_Store (State, X);
          when Reg_Load  => CPU.Reg_Load (State, X);
+         when Reg_Store_X => declare
+            I : constant Address := State.Address_Register;
+         begin
+            State.Address_Register := CPU.RPL_Stash;
+            CPU.Reg_Store (State, X);
+            State.Address_Register := I;
+         end;
+         when Reg_Load_X  => declare
+            I : constant Address := State.Address_Register;
+         begin
+            State.Address_Register := CPU.RPL_Stash;
+            CPU.Reg_Load (State, X);
+            State.Address_Register := I;
+         end;
          when Add_Address => State.Address_Register :=
             State.Address_Register + Address (State.Registers (X));
-         when Get_Font =>
-            State.Address_Register := Address (State.Registers (X) mod 16) * 5;
+         when Get_Font => declare
+            use Video;
+            VX : constant Byte := State.Registers (X);
+         begin
+            if Model = Video.Super_Chip_10 and then VX > 15 then
+               State.Address_Register := Address (VX mod 16) * 10 + 80;
+            else
+               State.Address_Register := Address (VX mod 16) * 5;
+            end if;
+         end;
+         when Get_Font_10 =>
+            State.Address_Register := Address (State.Registers (X) mod 16)
+               * 10 + 80;
          when Get_BCD =>
             State.Memory (State.Address_Register) :=
                State.Registers (X) / 100;
@@ -143,17 +206,46 @@ procedure Ada_Chip is
       end case;
    end Run_Step;
 
-   Steps_Per_Frame   : constant := 8;
    Beep_Sound        : constant Sf.Audio.sfSound_Ptr := Sf.Audio.Sound.create;
    Beep_Sound_Buffer : constant Sf.Audio.sfSoundBuffer_Ptr :=
       Sf.Audio.SoundBuffer.createFromFile ("beep.ogg");
+
+   File_Loaded : Boolean := False;
 begin
-   if Ada.Command_Line.Argument_Count /= 1 then
-      Ada.Text_IO.Put_Line ("usage: adachip <.c8 file>");
+   for I in 1 .. Ada.Command_Line.Argument_Count loop
+      declare
+         Arg : constant String := Ada.Command_Line.Argument (I);
+      begin
+         if Arg = "--schip" then
+            Ada.Text_IO.Put_Line ("Super-CHIP model selected.");
+            Model := Video.Super_Chip_10;
+         else
+            if Arg'Length > 6
+               and then Arg (1 .. 6) = "--spf="
+            then
+               Steps_Per_Frame := Natural'Value (Arg (7 .. Arg'Length));
+            else
+               if File_Loaded then
+                  Ada.Text_IO.Put_Line ("More than one ROM specified!");
+                  File_Loaded := False;
+                  exit;
+               else
+                  Ada.Text_IO.Put_Line ("Loading ROM: " &
+                     Ada.Command_Line.Argument (I));
+                  CPU.Load_File (State, Ada.Command_Line.Argument (I));
+                  File_Loaded := True;
+               end if;
+            end if;
+         end if;
+      end;
+   end loop;
+
+   if not File_Loaded then
+      Ada.Text_IO.Put_Line ("usage: adachip [flags] <.c8 file>");
    else
-      Video.Initialize;
+      Video.Initialize (Model);
+      Video.Low_Res;
       Random_Byte.Reset (Random_Generator);
-      CPU.Load_File (State, Ada.Command_Line.Argument (1));
       Sf.Audio.Sound.setBuffer (Beep_Sound, Beep_Sound_Buffer);
 
       while Video.Is_Running loop
diff --git a/src/cpu.ads b/src/cpu.ads
index 340375b..5401698 100644
--- a/src/cpu.ads
+++ b/src/cpu.ads
@@ -25,6 +25,23 @@ package CPU is
          16#E0#, 16#90#, 16#90#, 16#90#, 16#E0#, -- D
          16#F0#, 16#80#, 16#F0#, 16#80#, 16#F0#, -- E
          16#F0#, 16#80#, 16#F0#, 16#80#, 16#80#, -- F
+
+         16#F0#, 0, 16#90#, 0, 16#90#, 0, 16#90#, 0, 16#F0#, 0, -- 0
+         16#20#, 0, 16#60#, 0, 16#20#, 0, 16#20#, 0, 16#70#, 0, -- 1
+         16#F0#, 0, 16#10#, 0, 16#F0#, 0, 16#80#, 0, 16#F0#, 0, -- 2
+         16#F0#, 0, 16#10#, 0, 16#F0#, 0, 16#10#, 0, 16#F0#, 0, -- 3
+         16#90#, 0, 16#90#, 0, 16#F0#, 0, 16#10#, 0, 16#10#, 0, -- 4
+         16#F0#, 0, 16#80#, 0, 16#F0#, 0, 16#10#, 0, 16#F0#, 0, -- 5
+         16#F0#, 0, 16#80#, 0, 16#F0#, 0, 16#90#, 0, 16#F0#, 0, -- 6
+         16#F0#, 0, 16#10#, 0, 16#20#, 0, 16#40#, 0, 16#40#, 0, -- 7
+         16#F0#, 0, 16#90#, 0, 16#F0#, 0, 16#90#, 0, 16#F0#, 0, -- 8
+         16#F0#, 0, 16#90#, 0, 16#F0#, 0, 16#10#, 0, 16#F0#, 0, -- 9
+         16#F0#, 0, 16#90#, 0, 16#F0#, 0, 16#90#, 0, 16#90#, 0, -- A
+         16#E0#, 0, 16#90#, 0, 16#E0#, 0, 16#90#, 0, 16#E0#, 0, -- B
+         16#F0#, 0, 16#80#, 0, 16#80#, 0, 16#80#, 0, 16#F0#, 0, -- C
+         16#E0#, 0, 16#90#, 0, 16#90#, 0, 16#90#, 0, 16#E0#, 0, -- D
+         16#F0#, 0, 16#80#, 0, 16#F0#, 0, 16#80#, 0, 16#F0#, 0, -- E
+         16#F0#, 0, 16#80#, 0, 16#F0#, 0, 16#80#, 0, 16#80#, 0, -- F
          others => 0
       );
       Registers        : Register_Bank;
@@ -33,6 +50,8 @@ package CPU is
       Stack            : Address_Stack.Vector;
    end record;
 
+   RPL_Stash : constant Address := 240;
+
    procedure Load_File (Inst : in out Instance; File_Name : String);
 
    function Get_Opcode (Inst : in out Instance) return Opcode;
diff --git a/src/isa.ads b/src/isa.ads
index ffc0593..e5310d3 100644
--- a/src/isa.ads
+++ b/src/isa.ads
@@ -45,6 +45,8 @@ package ISA is
 
    Clear_Screen : constant Opcode_Value := 16#E0#;
    Ret          : constant Opcode_Value := 16#EE#;
+   Low_Res      : constant Opcode_Value := 16#FE#;
+   High_Res     : constant Opcode_Value := 16#FF#;
 
    type Math_Class is (
       Assign, Bit_Or, Bit_And, Bit_Xor, Add, Sub_Y, Shift_Right, Sub_X,
@@ -71,8 +73,8 @@ package ISA is
    );
 
    type Misc_Class is (
-      Get_Delay, Get_Key, Set_Delay, Set_Sound, Add_Address, Get_Font, Get_BCD,
-      Reg_Store, Reg_Load
+      Get_Delay, Get_Key, Set_Delay, Set_Sound, Add_Address, Get_Font,
+      Get_Font_10, Get_BCD, Reg_Store, Reg_Load, Reg_Store_X, Reg_Load_X
    );
 
    for Misc_Class use (
@@ -82,9 +84,12 @@ package ISA is
       Set_Sound   => Opcode_Value (16#18#),
       Add_Address => Opcode_Value (16#1E#),
       Get_Font    => Opcode_Value (16#29#),
+      Get_Font_10 => Opcode_Value (16#30#),
       Get_BCD     => Opcode_Value (16#33#),
       Reg_Store   => Opcode_Value (16#55#),
-      Reg_Load    => Opcode_Value (16#65#)
+      Reg_Load    => Opcode_Value (16#65#),
+      Reg_Store_X => Opcode_Value (16#75#),
+      Reg_Load_X  => Opcode_Value (16#85#)
    );
 
    Start_Address : constant Address := 16#200#;
diff --git a/src/video.adb b/src/video.adb
index aebf23d..f4ddb6b 100644
--- a/src/video.adb
+++ b/src/video.adb
@@ -2,22 +2,55 @@ with Sf.Window;
 with Sf.Window.Event;
 with Sf.Window.Keyboard;
 with Sf.Graphics.Color;
+with Sf.Graphics.Image;
+with Sf.Graphics.RenderWindow;
+with Sf.Graphics.Texture;
 
 package body Video is
    procedure Clear_Screen is
    begin
-      for X in 0 .. sfUint32 (Width) - 1 loop
-         for Y in 0 .. sfUint32 (Height) - 1 loop
+      for X in 0 .. Width - 1 loop
+         for Y in 0 .. Height - 1 loop
             Image.setPixel (Pixels, X, Y, Color.sfBlack);
          end loop;
       end loop;
    end Clear_Screen;
 
-   procedure Initialize is
+   procedure Low_Res is
    begin
+      Video.Width := 64;
+      Video.Height := 32;
+      Video.Scale := 10;
+      Sprite.setScale (Pixels_Sprite, (Float (Scale), Float (Scale)));
+   end Low_Res;
+
+   procedure High_Res is
+   begin
+      Video.Width := 128;
+      Video.Height := 64;
+      Video.Scale := 5;
+      Sprite.setScale (Pixels_Sprite, (Float (Scale), Float (Scale)));
+   end High_Res;
+
+   procedure Initialize (M : Model) is
+   begin
+      case M is
+         when Chip_8 =>
+            Width  := 64;
+            Height := 32;
+            Scale  := 10;
+         when Super_Chip_10 =>
+            Width  := 128;
+            Height := 64;
+            Scale  := 5;
+      end case;
+
+      app := RenderWindow.create ((Width * Scale, Height * Scale, 32), Title);
+      Pixels := Image.create (Width, Height);
+      Pixels_Texture := Texture.createFromImage (Pixels);
+
       Sprite.setTexture (Pixels_Sprite, Pixels_Texture);
       Sprite.setPosition (Pixels_Sprite, (Float (0), Float (0)));
-      Sprite.setScale (Pixels_Sprite, (Float (Scale), Float (Scale)));
       RenderWindow.setFramerateLimit (app, 60);
    end Initialize;
 
diff --git a/src/video.ads b/src/video.ads
index 32f7683..ec9b212 100644
--- a/src/video.ads
+++ b/src/video.ads
@@ -1,24 +1,25 @@
 with Sf;
 with Sf.Graphics;
-with Sf.Graphics.Image;
-with Sf.Graphics.RenderWindow;
 with Sf.Graphics.Sprite;
-with Sf.Graphics.Texture;
 
 package Video is
    use Sf;
    use Sf.Graphics;
 
+   type Model is (Chip_8, Super_Chip_10);
    type Key is range 0 .. 15;
    type Key_Map is array (Key'Range) of Boolean;
 
-   Width  : constant := 64;
-   Height : constant := 32;
-   Scale  : constant := 10;
    Title  : constant String := "Ada-Chip";
 
+   Width  : sfUint32;
+   Height : sfUint32;
+   Scale  : sfUint32;
+
    procedure Clear_Screen;
-   procedure Initialize;
+   procedure Low_Res;
+   procedure High_Res;
+   procedure Initialize (M : Model);
    procedure Display;
    procedure Finish;
    procedure Poll_Events;
@@ -31,11 +32,8 @@ package Video is
 
 private
    Keys           : Key_Map;
-   Pixels         : constant sfImage_Ptr   := Image.create (Width, Height);
-   Pixels_Sprite  : constant sfSprite_Ptr  := Sprite.create;
-   Pixels_Texture : constant sfTexture_Ptr :=
-      Texture.createFromImage (Pixels);
-
-   app : constant sfRenderWindow_Ptr := RenderWindow.create
-      ((Width * Scale, Height * Scale, 32), Title);
+   app            : sfRenderWindow_Ptr;
+   Pixels         : sfImage_Ptr;
+   Pixels_Texture : sfTexture_Ptr;
+   Pixels_Sprite  : constant sfSprite_Ptr := Sprite.create;
 end Video;
-- 
cgit v1.2.3