251 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			251 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
 | 
						|
{-----------------------------------------------------------------------+
 | 
						|
|                                                                       |
 | 
						|
|       Recreation of WozMon for Gigatron TTL microcomputer             |
 | 
						|
|                                                                       |
 | 
						|
+-----------------------------------------------------------------------}
 | 
						|
gcl0x
 | 
						|
 | 
						|
{
 | 
						|
  2018-06-12 (marcelk) Initial version
 | 
						|
  2018-06-15 (marcelk) Mimic behaviour of original much better, while
 | 
						|
                       avoiding spurious newlines to save vertical space.
 | 
						|
                       Gracefully allow self-invocation ("200R").
 | 
						|
  2018-06-21 (marcelk) Simplifications ported back from Tiny BASIC
 | 
						|
  2018-06-22 (marcelk) Use the won space to echo user input in white
 | 
						|
  2018-07-29 (marcelk) Accept up to 25 characters from input (was 24)
 | 
						|
 | 
						|
  What works the same as the original:
 | 
						|
    - Type hex address to examine memory contents
 | 
						|
    - Type '.' and end address for block examine
 | 
						|
    - Type ':' and data bytes for writing into memory
 | 
						|
    - Type 'R' to start execution from last address
 | 
						|
    - Multiple commands in one line
 | 
						|
    - Any ASCII code below '.' is a blank
 | 
						|
    - Too long input is flushed
 | 
						|
    - Shows backslash prompt at error ('\')
 | 
						|
 | 
						|
  Gigatron-specific differences:
 | 
						|
    - Welcome message is "*** WozMon" instead of error prompt ('\')
 | 
						|
    - Can use lowercase as well as uppercase for hex letters
 | 
						|
    - Delete shows effect on screen, no need for '_' (RUBOUT)
 | 
						|
    - Always print address at start of line in block examine mode
 | 
						|
    - Input buffer is bounded by the screen width
 | 
						|
    - Executed code can go back to monitor with RET instruction ($ff)
 | 
						|
    - Faster screen update, and cursor symbol instead of '@'
 | 
						|
    - A bit more careful with emitting empty lines
 | 
						|
 | 
						|
  References:
 | 
						|
 | 
						|
  Online Apple 1 Emulator (Hit [RESET] to enter WozMon):
 | 
						|
        https://www.scullinsteel.com/apple1/
 | 
						|
 | 
						|
  Using The Woz Monitor:
 | 
						|
        https://www.sbprojects.net/projects/apple1/wozmon.php
 | 
						|
 | 
						|
  Original MOS6502 source code:
 | 
						|
        https://github.com/jefftranter/6502/tree/master/asm/wozmon
 | 
						|
        https://www.sbprojects.com/projects/apple1/wozmon.txt
 | 
						|
}
 | 
						|
 | 
						|
{-----------------------------------------------------------------------+
 | 
						|
|                       RAM page 2                                      |
 | 
						|
+-----------------------------------------------------------------------}
 | 
						|
 | 
						|
[def $2a# $2a# $20# $57# $6f# $7a# $4d# $6f# $6e# 0#] tmp= {** WozMon}
 | 
						|
 | 
						|
$01df deek Pos=                 {Bottom character row in screen memory}
 | 
						|
                                {Slightly cheating with endianness}
 | 
						|
 | 
						|
{ Print ASCII character (32..127) on screen in 5x8 pixels }
 | 
						|
{ Also advance cursor position }
 | 
						|
[def
 | 
						|
  82-                           {Map ASCII code to offset in font table}
 | 
						|
  [if<0 50+ i= \font32up        {ASCII 32..81}
 | 
						|
   else     i= \font82up] k=    {ASCII 82..127}
 | 
						|
  i 2<< i+                      {Multiply by 5}
 | 
						|
  k+ k=                         {Add to page address to reach bitmap data}
 | 
						|
 | 
						|
  \SYS_VDrawBits_134 \sysFn:    {Prepare SYS calls}
 | 
						|
  \sysArgs6; \sysArgs0:         {Apply caller-defined colors}
 | 
						|
  Pos \sysArgs4:                {Position for character}
 | 
						|
  $fe%=                         {Temporarily park return value on the stack}
 | 
						|
  6+ Pos=                       {Advance position by 6 pixels}
 | 
						|
 | 
						|
  5 [do i=                      {Draw 5 vertical slices}
 | 
						|
    k 0? \sysArgs2.             {Get slice from ROM}
 | 
						|
    134!!                       {Invoke SYS function to draw pixels}
 | 
						|
    k<++ \sysArgs4<++           {Advance to next slice}
 | 
						|
    i 1- if>0loop]              {Looping}
 | 
						|
 | 
						|
  $fe%                          {Return effective position}
 | 
						|
  ret
 | 
						|
] PrintChar=
 | 
						|
 | 
						|
{ Print a newline conditionally }
 | 
						|
[def
 | 
						|
  Pos<, [if<>0                  {If not at start already}
 | 
						|
    \SYS_VDrawBits_134 \sysFn:  {Prepare SYS call}
 | 
						|
    $800 Pos<.                  {Go back to start}
 | 
						|
    Pos+ [if<0 $0800] Pos=      {Go down 8 lines and wrap around if needed}
 | 
						|
    \sysArgs4:                  {sysArgs[4:5] is position on screen}
 | 
						|
    \sysArgs2.                  {All-zero output pattern}
 | 
						|
    [do
 | 
						|
      134!!                     {SYS call}
 | 
						|
      \sysArgs4<++              {Advance to next slice}
 | 
						|
      \sysArgs4, 160^           {Test for end of screen}
 | 
						|
      if<>0loop]
 | 
						|
                                {Scroll up by modifying videoTable}
 | 
						|
    $01ee i=                    {Last entry in video table}
 | 
						|
    [do
 | 
						|
      i, 120- [if<0 128^
 | 
						|
               else 8^]
 | 
						|
               i.               {Rotate by 8 in 8..127 range}
 | 
						|
      i 2- i=                   {Previous entry in video table}
 | 
						|
      $fe^ if<>0loop]           {Until all done}
 | 
						|
  ]
 | 
						|
  ret
 | 
						|
] CNewline=
 | 
						|
 | 
						|
{ Print byte as hexadecimal value }
 | 
						|
[def
 | 
						|
  push
 | 
						|
  tmp=                          {Save value}
 | 
						|
  \SYS_LSRW4_50 \sysFn:         {Prepare SYS call for shift}
 | 
						|
  tmp 50!!                      {Shift right 4 bits}
 | 
						|
  10- [if>=0 7+] $3a+           {Convert to hex digit}
 | 
						|
  PrintChar!
 | 
						|
  tmp 15&                       {Low 4 bits}
 | 
						|
  10- [if>=0 7+] $3a+           {Convert to hex digit}
 | 
						|
  PrintChar!
 | 
						|
  pop ret
 | 
						|
] PrintByte=
 | 
						|
 | 
						|
$300 call
 | 
						|
{-----------------------------------------------------------------------+
 | 
						|
|                       RAM page 3                                      |
 | 
						|
+-----------------------------------------------------------------------}
 | 
						|
$0300:
 | 
						|
 | 
						|
{ Process new input line }
 | 
						|
[def
 | 
						|
  push
 | 
						|
  $80 Buffer=                   {One before begin of buffer}
 | 
						|
  CNewline!
 | 
						|
  [do {NEXTITEM}
 | 
						|
    Buffer<++                   {Advance text pointer}
 | 
						|
    Buffer, if<>0               {While not at end of buffer}
 | 
						|
 | 
						|
    $2e- if<0loop               {Ignore everything below '.'}
 | 
						|
         [if=0  1 Mode= loop]   {'.' Set Block Examine mode and continue}
 | 
						|
    $0c- [if=0 1- Mode= loop]   {':' Set Store mode and continue}
 | 
						|
    $18- [if=0 Examine! loop]   {'R' "Run" and continue in case of return}
 | 
						|
 | 
						|
    0 tmp=                      {Stays zero if we don't see any hex digit}
 | 
						|
    [do {NEXTHEX}               {Parsing hexadecimal number}
 | 
						|
      Value=                    {Update partial result}
 | 
						|
      4<< i=                    {Next term for adding hex digit}
 | 
						|
      Buffer,                   {Get character for hex test}
 | 
						|
      Buffer<++                 {Already advance text pointer}
 | 
						|
      $30- {'0'} if>=0          {Bail out if out of range}
 | 
						|
      10-  {'9'} [if<0          {Decimal digit}
 | 
						|
        tmp=                    {Non-zero to mark hex digits found}
 | 
						|
        10+                     {Map in 0..9 range}
 | 
						|
        i+                      {Add it to value}
 | 
						|
        loop]                   {To NEXTHEX}
 | 
						|
      6- 31&                    {Map remainder to uppercase}
 | 
						|
         {'A'} if>0             {Bail out if out of range}
 | 
						|
      7- {'F'} if<0             {Hex letter}
 | 
						|
        tmp=                    {Non-zero to mark hex digits found}
 | 
						|
        16+                     {Map in 10..15 range}
 | 
						|
        i+                      {Add it to value}
 | 
						|
        loop]                   {To NEXTHEX}
 | 
						|
 | 
						|
    tmp [if<>0                  {There is a value to process}
 | 
						|
 | 
						|
      Buffer 2- Buffer=         {We advanced too much}
 | 
						|
 | 
						|
      Mode [if<0                {"Store"}
 | 
						|
        Value Store.            {Store value through store pointer}
 | 
						|
        Store 1+ Store=         {Advance store pointer}
 | 
						|
        loop]                   {To NEXTITEM}
 | 
						|
 | 
						|
      [if=0                     {"Examine"}
 | 
						|
        CNewline!               {Forces printing of new address}
 | 
						|
        Value Store=            {Also set store pointer}
 | 
						|
        1- Examine=]            {Will print one value}
 | 
						|
 | 
						|
      [do                       {"Block Examine"}
 | 
						|
        Examine Value- if<0     {Walk with Examine until past Value}
 | 
						|
        Examine 1+ Examine=     {Advance examine pointer}
 | 
						|
        7& [if=0 CNewline!]     {If address mod 8 == 0 continue on next line}
 | 
						|
        Pos<, [if=0             {If at start of line print address}
 | 
						|
          2 Pos<.               {Tiny 2 pixel indent to be nice}
 | 
						|
          Examine>, PrintByte!  {Print high-byte of address}
 | 
						|
          Examine<, PrintByte!  {Print low-byte of address}
 | 
						|
          $3a PrintChar!]       {Print colon}
 | 
						|
        Pos 4+ Pos=             {Small 4 pixel space to fit on Gigatron screen}
 | 
						|
        Examine, PrintByte!     {Print data byte at address}
 | 
						|
        loop]
 | 
						|
      0 Mode=                   {Back to Examine mode}
 | 
						|
      loop]                     {To NEXTITEM}
 | 
						|
 | 
						|
    $5c PrintChar!]             {Error prompt '\' and break loop}
 | 
						|
 | 
						|
  pop ret
 | 
						|
] ProcessBuffer=
 | 
						|
 | 
						|
Pos<++                          {Force a non-zero low byte}
 | 
						|
$0f20 \sysArgs6:                {Pen color yellow, background color blue}
 | 
						|
\sysArgs0. CNewline!            {Now forced to scroll and clear last line}
 | 
						|
 | 
						|
{ Display '*' and welcome message }
 | 
						|
$2a [do PrintChar! tmp, tmp<++ if<>0loop]
 | 
						|
 | 
						|
$0400 call
 | 
						|
{-----------------------------------------------------------------------+
 | 
						|
|                       RAM page 4                                      |
 | 
						|
+-----------------------------------------------------------------------}
 | 
						|
$0400:
 | 
						|
 | 
						|
{ Input loop, assume some kind of ASCII keyboard is hooked up }
 | 
						|
 | 
						|
[do {GETLINE}
 | 
						|
  $81 Buffer=                   {Prepare for next input line}
 | 
						|
  CNewline!
 | 
						|
 | 
						|
  [do {NEXTCHAR}
 | 
						|
    127 \sysArgs7.              {Pen color white for user input}
 | 
						|
    PrintChar! Pos=             {Draw cursor}
 | 
						|
 | 
						|
    \serialRaw, [do             {Wait for key change}
 | 
						|
      tmp= \serialRaw, Buffer.
 | 
						|
      tmp^ if=0 Buffer, loop]
 | 
						|
 | 
						|
    Buffer, 10^ if<>0           {Enter/return breaks NEXTCHAR loop}
 | 
						|
 | 
						|
    117^ [if=0                  {Delete pressed (10^127 == 117)}
 | 
						|
      $20 PrintChar! Pos=       {Remove cursor}
 | 
						|
      Pos<, 6- [if>=0           {If not on first position}
 | 
						|
        Pos<.                   {Step back}
 | 
						|
        Buffer 1- Buffer=]      {Also remove character from buffer}
 | 
						|
      loop]                     {To NEXTCHAR}
 | 
						|
 | 
						|
    96- if>=0loop               {Ignore apparent unprintable garbage}
 | 
						|
 | 
						|
    Pos<, 150^ [if=0            {Arrived at position 25 (25*6 == 150)}
 | 
						|
      $5c PrintChar!            {Error indicator '\'}
 | 
						|
      $400 call]                {To GETLINE and discard too long input}
 | 
						|
 | 
						|
    Buffer, PrintChar!          {Print accepted characters}
 | 
						|
    Buffer<++                   {Advance pointer, keeping the character}
 | 
						|
 | 
						|
    loop]                       {To NEXTCHAR}
 | 
						|
 | 
						|
  Buffer.                       {Terminate input with zero}
 | 
						|
  Mode=                         {Start in examine mode}
 | 
						|
  $20 PrintChar!                {Remove and advance cursor}
 | 
						|
  $0f \sysArgs7.                {Pen color yellow for output}
 | 
						|
  ProcessBuffer!
 | 
						|
  loop]                         {To GETLINE}
 |