module ZXUtils::MultitaskingIO::Macros

ZXUtils::MultitaskingIO Macros for tasks.

Most of the routines created by MultitaskingIO::Macros expects an I/O buffer handle in the hl register pair. The buffer handle may be obtained by calling one of the kernel functions:

Public Instance Methods

mtio_drain(disable_intr:true, enable_intr:true) click to toggle source

Drains the I/O buffer.

Options:

  • disable_intr

    a boolean flag indicating that the routine should disable interrupts. Provide false only if you have already disabled the interrupts.

  • enable_intr

    a boolean flag indicating that the routine should enable interrupts. Provide false if you need to perform more atomic actions.

Expects:

  • hl

    an I/O buffer handle.

Removes all the pending data from an I/O buffer.

Modifies: af, preserves: hl.

# File lib/zxutils/multitasking_io.rb, line 438
def mtio_drain(disable_intr:true, enable_intr:true)
  isolate do |eoc|
                    dec   hl
                    di if disable_intr
                    ld    a, [hl] # outoffs
                    inc   hl
                    ei if enable_intr
                    ld    [hl], a # inpoffs
  end
end
mtio_getc(char=e, tt:bc, not_ready: :eoc, subroutine: true, preserve_hl:true, disable_intr:true, enable_intr:true, mtyield: task_yield) click to toggle source

Reads a single character from the I/O buffer. Arguments:

  • char

    an 8 bit register which should receive a code if the character read.

Options:

  • tt

    an 16 bit register for temporary use.

  • not_ready

    what to do when a character can't be read. A symbol :eoc to just signal the failure with the CF flag reset (NC) and the ZF flag set (Z). A symbol :wait to wait for the character to become available. A label or an address to jump to when the character is not available.

  • subroutine

    if the routine should terminate with a ret instruction.

  • preserve_hl

    if the routine should preserve the hl register via the stack.

  • disable_intr

    a boolean flag indicating that the routine should disable interrupts. Provide false only if you have already disabled the interrupts.

  • enable_intr

    a boolean flag indicating that the routine should enable interrupts. Provide false if you need to perform more atomic actions.

  • mtyield

    should contain an address of the kernel routine: task_yield.

Expects:

  • hl

    an I/O buffer handle.

On success the CF flag is set and a register char contains a read character.

Modifies: af, hl, tt, char, optionally preserves: hl on the machine stack.

# File lib/zxutils/multitasking_io.rb, line 472
def mtio_getc(char=e, tt:bc, not_ready: :eoc, subroutine: true, preserve_hl:true, disable_intr:true, enable_intr:true, mtyield: task_yield)
  th, tl = tt.split
  raise ArgumentError, "char must not be a tl or th or h or l" if [h,l,th,tl].include?(char)
  isolate do
                    push  hl if preserve_hl
      repeat        label
                    di if disable_intr
                    ld    tl, [hl] # inpoffs
                    dec   hl
                    ld    a, [hl]  # outoffs
                    cp    tl
    case not_ready
    when :eoc
      if subroutine && !enable_intr && !preserve_hl
                    ret   Z
      else
                    jr    Z, eop
      end
    when :wait
      if subroutine
                    jr    Z, wait_busy
      else
                    jr    NZ, skip
                    call  mtyield
                    di unless disable_intr
                    inc   hl
                    jr    repeat
      end
    else
                    jp    Z, not_ready
    end
    skip            inc   tl
                    inc   hl
                    ld    [hl], tl # inpoffs
                    dec   hl       # outoffs
                    ld    th, -1
                    add   hl, tt   # CF=1
                    ld    char, [hl]
    eop             label
                    ei if enable_intr
                    pop   hl if preserve_hl
    if subroutine
                    ret
      if not_ready == :wait
    wait_busy       call  mtyield
                    di unless disable_intr
                    inc   hl
                    jr    repeat
      end
    end
  end
end
mtio_gets(nchars=a, check_nchars_zero:true, subroutine:true, disable_intr:true, enable_intr:true, mtyield: task_yield) click to toggle source

Reads a string of characters from the I/O buffer.

Arguments:

  • nchars

    a number 1..255 or accumulator with a number of characters to read.

Options:

  • tt

    an 16 bit register for temporary use.

  • check_nchars_zero

    a boolean indicating if the accumulator should be checked against 0.

  • subroutine

    if the routine should terminate with a ret instruction.

  • disable_intr

    a boolean flag indicating that the routine should disable interrupts. Provide false only if you have already disabled the interrupts.

  • enable_intr

    a boolean flag indicating that the routine should enable interrupts. Provide false if you need to perform more atomic actions.

  • mtyield

    should contain an address of the kernel routine: task_yield.

Expects:

  • hl

    an I/O buffer handle.

  • de

    an address where the string will be stored.

On success accumulator contains the number of characters successfully read. If its value is 0 the ZF flag is also set indicating that no characters has been read at all. Preserves hl registers. de will be incremented by the number of characters read.

Modifies: af, af', de, bc and uses stack to preserve hl.

# File lib/zxutils/multitasking_io.rb, line 625
def mtio_gets(nchars=a, check_nchars_zero:true, subroutine:true, disable_intr:true, enable_intr:true, mtyield: task_yield)
  raise ArgumentError, "nchars must be accumulator or an integer: 1..255" unless nchars == a or (1..255).include?(nchars)
  isolate do |eoc|
    if nchars == a
      if check_nchars_zero
                    anda  a         # nchars == 0
        if subroutine
                    ret   Z
        else
                    jr    Z, eoc
        end
      end
                    ex    af, af
    end
                    di if disable_intr
                    dec   hl        # -> outoffs
                    ld    a, [hl]   # outoffs
                    inc   hl        # -> inpoffs
                    ld    c, [hl]   # inpoffs
                    sub   c         # a: nchars ready
                    jr    Z, eop    # nothing to read
                    inc   c         # inpoffs + 1
                    push  hl        # -> inpoffs
                    dec   hl        # -> outoffs
                    ld    b, -1
                    add   hl, bc
                    ld    b, a      # b: nchars ready
    if nchars == a
                    ex    af, af
    else
                    ld    a, nchars
    end
                    cp    b         # nchars < nchars ready
                    jr    NC, nchars_not_less
                    ld    b, a      # counter = nchars
    nchars_not_less ld    a, c      # a: inpoffs
                    ld    c, b      # c: counter min(nchars, nchars ready)
                    dec   b         # b: counter - 1
                    add   b         # inpoffs = (inpoffs + counter - 1) % 256
                    jr    NC, one_pass_only
                    ld    b, a      # save inpoffs
                    cpl             # (-inpoffs - 1) % 256
                    add   c         # ((-inpoffs - 1) % 256 + counter) % 256, CF: 1
                    ld    c, a      # reduced counter to the end of the i/o buffer
                    ld    a, b      # restore inpoffs
    one_pass_only   ld    b, 0
                    ldir            # HL: -> buffer, DE: -> target
                    jr    NC, no_2nd_pass
                    ld    c, a
                    inc   c         # counter = inpoffs + 1
                    dec   h         # rewind buffer
                    ldir            # DE: -> source + min(nchars, nchars ready)
    no_2nd_pass     pop   hl        # -> inpoffs
                    ld    c, [hl]   # prev inpoffs
                    ld    [hl], a   # new inpoffs
                    sub   c         # written = new inpoffs - prev inpoffs
    eop             label
                    ei if enable_intr
                    ret if subroutine
  end
end
mtio_putc(char=e, tt:bc, not_ready: :eoc, subroutine: true, preserve_hl:true, disable_intr:true, enable_intr:true, mtyield: task_yield) click to toggle source

Writes a single character to the I/O buffer.

Arguments:

  • char

    a number or an 8 bit register with the character code to be written.

Options:

  • tt

    an 16 bit register for temporary use.

  • not_ready

    what to do when a character can't be written. A symbol :eoc to just signal the failure with the CF flag reset (NC) and the ZF flag set (Z). A symbol :wait to wait until a character gets written. A label or an address to jump to when the character can't be written.

  • subroutine

    if the routine should terminate with a ret instruction.

  • preserve_hl

    if the routine should preserve the hl register via the stack.

  • disable_intr

    a boolean flag indicating that the routine should disable interrupts. Provide false only if you have already disabled the interrupts.

  • enable_intr

    a boolean flag indicating that the routine should enable interrupts. Provide false if you need to perform more atomic actions.

  • mtyield

    should contain an address of the kernel routine: task_yield.

Expects:

  • hl

    an I/O buffer handle.

On success the CF flag is set and the ZF flag is clear (NZ).

Modifies: af, hl, tt, preserves: char, optionally preserves: hl on the machine stack.

# File lib/zxutils/multitasking_io.rb, line 549
def mtio_putc(char=e, tt:bc, not_ready: :eoc, subroutine: true, preserve_hl:true, disable_intr:true, enable_intr:true, mtyield: task_yield)
  th, tl = tt.split
  raise ArgumentError, "char must not be a tl or th or h or l" if [h,l,th,tl].include?(char)
  isolate do
                    push  hl if preserve_hl
                    ld    th, a if char==a
      repeat        label
                    di if disable_intr
                    ld    a, [hl]   # inpoffs
                    dec   hl
                    ld    tl, [hl]  # outoffs
                    inc   tl
                    cp    tl
    case not_ready
    when :eoc
      if subroutine && !enable_intr && !preserve_hl
                    ret   Z
      else
                    jr    Z, eop
      end
    when :wait
      if subroutine
                    jr    Z, wait_busy
      else
                    jr    NZ, skip
                    call  mtyield
                    di unless disable_intr
                    inc   hl
                    jr    repeat
      end
    else
                    jp    Z, not_ready
    end
    skip            ld    [hl], tl  # outoffs
                    ld    a, th if char==a
                    ld    th, -1
                    add   hl, tt
                    ld    [hl], char
    eop             label
                    ei if enable_intr
                    pop   hl if preserve_hl
    if subroutine
                    ret
      if not_ready == :wait
    wait_busy       call  mtyield
                    di unless disable_intr
                    inc   hl
                    jr    repeat
      end
    end
  end
end
mtio_puts(nchars=a, check_nchars_zero:true, subroutine:true, disable_intr:true, enable_intr:true, mtyield: task_yield) click to toggle source

Sends a string of characters to the I/O buffer.

Arguments:

  • nchars

    a number 1..255 or accumulator with a number of characters to be sent.

Options:

  • tt

    an 16 bit register for temporary use.

  • check_nchars_zero

    a boolean indicating if the accumulator should be checked against 0.

  • subroutine

    if the routine should terminate with a ret instruction.

  • disable_intr

    a boolean flag indicating that the routine should disable interrupts. Provide false only if you have already disabled the interrupts.

  • enable_intr

    a boolean flag indicating that the routine should enable interrupts. Provide false if you need to perform more atomic actions.

  • mtyield

    should contain an address of the kernel routine: task_yield.

Expects:

  • hl

    an I/O buffer handle.

  • de

    an address of the string to be send.

On success accumulator contains the number of characters successfully sent. If its value is 0 the ZF flag is also set indicating that no characters has been sent at all. Preserves hl registers. de will be incremented by the number of characters sent.

Modifies: af, af', de, bc and uses stack to preserve hl.

# File lib/zxutils/multitasking_io.rb, line 710
def mtio_puts(nchars=a, check_nchars_zero:true, subroutine:true, disable_intr:true, enable_intr:true, mtyield: task_yield)
  raise ArgumentError, "nchars must be accumulator or an integer: 1..255" unless nchars == a or (1..255).include?(nchars)
  isolate do |eoc|
    if nchars == a
      if check_nchars_zero
                    anda  a         # nchars == 0
        if subroutine
                    ret   Z
        else
                    jr    Z, eoc
        end
      end
                    ex    af, af
    end
                    di if disable_intr
                    ld    a, [hl]   # -> inpoffs
                    dec   hl
                    ld    c, [hl]   # -> outoffs
                    inc   c         # c: outoffs + 1
                    sub   c         # a: buffer free
                    jr    Z, eop    # no room to write
                    push  hl        # -> outoffs
                    ld    b, -1
                    add   hl, bc
                    ld    b, a      # b: buffer free
    if nchars == a
                    ex    af, af
    else
                    ld    a, nchars
    end
                    cp    b         # nchars < buffer free
                    jr    NC, nchars_not_less
                    ld    b, a      # counter = nchars
    nchars_not_less ld    a, c      # a: outoffs
                    ld    c, b      # c: counter min(nchars, buffer free)
                    dec   b         # b: counter - 1
                    add   b         # outoffs = (outoffs + counter - 1) % 256
                    jr    NC, one_pass_only
                    ld    b, a      # save outoffs
                    cpl             # (-outoffs - 1) % 256
                    add   c         # ((-outoffs - 1) % 256 + counter) % 256, CF: 1
                    ld    c, a      # reduced counter to the end of the i/o buffer
                    ld    a, b      # restore outoffs
    one_pass_only   ld    b, 0
                    ex    de, hl    # DE: -> buffer, HL: -> source
                    ldir
                    jr    NC, no_2nd_pass
                    ld    c, a
                    inc   c         # counter = outoffs + 1
                    dec   d         # rewind buffer
                    ldir
    no_2nd_pass     ex    de, hl    # DE: -> source + min(nchars, buffer free)
                    pop   hl        # -> outoffs
                    ld    c, [hl]   # prev outoffs
                    ld    [hl], a   # new outoffs
                    sub   c         # written = new outoffs - prev outoffs
    eop             inc   hl        # -> inpoffs
                    ei if enable_intr
                    ret if subroutine
  end
end
mtio_ready?(action, nchars:nil, disable_intr:true, enable_intr:true) click to toggle source

Checks I/O buffer's data availability.

Arguments:

  • action

    a symbol :read to get the information if the data is ready to be read, a symbol :write to get the information if the data can be written.

  • nchars

    nil to get a simple boolean answer or a number or an 8 bit register which should contain a number of characters that should be available for reading or writing.

Options:

  • disable_intr

    a boolean flag indicating that the routine should disable interrupts. Provide false only if you have already disabled the interrupts.

  • enable_intr

    a boolean flag indicating that the routine should enable interrupts. Provide false if you need to perform more atomic actions.

Expects:

  • hl

    an I/O buffer handle.

Depending on the selected action and whether nchars option was nil or not, on return:

action    nchars      status     flags      accumulator
 *any*       nil      not ready  ZF=1 (Z)   0
 :read       nil      ready      ZF=0 (NZ)  no of characters ready to be read <= 255
:write       nil      ready      ZF=0 (NZ)  no of characters already written + 1 <= 255
 :read    number      not ready  CF=1 (C)   no of characters ready to be read < nchars
 :read    number      ready      CF=0 (NC)  no of characters ready to be read >= nchars
:write    number      not ready  CF=1 (C)   no of characters that won't fit into the buffer - 1 < nchars
:write    number      ready      CF=0 (NC)  no of characters already written + nchars <= 255

Modifies: af, preserves: hl and nchars.

# File lib/zxutils/multitasking_io.rb, line 369
def mtio_ready?(action, nchars:nil, disable_intr:true, enable_intr:true)
  raise ArgumentError, "action must be one of: :read or :write" unless action==:read or action==:write
  raise ArgumentError, "nchars must not be one of: a, h nor l" if [a, h, l].include?(nchars)
  isolate do
                    dec   hl
                    di if disable_intr
                    ld    a, [hl] # outoffs
                    inc   hl
                    ei if enable_intr
                    sub   [hl] # inpoffs
    if nchars
      case action
      when :read
                    cp   nchars
      when :write
                    add  nchars
      end
    else
                    inc   a if action == :write
    end
  end
end
mtio_wait(action, nchars=1, disable_intr:true, enable_intr:true, mtyield:task_yield) click to toggle source

Waits for the I/O buffer's data availability.

Arguments:

  • action

    a symbol :read to wait for the data to be read, a symbol :write to wait for the data to be written.

  • nchars

    a number or an 8 bit register which should contain a number of characters that should be available for reading or writing.

Options:

  • disable_intr

    a boolean flag indicating that the routine should disable interrupts. Provide false only if you have already disabled the interrupts.

  • enable_intr

    a boolean flag indicating that the routine should enable interrupts. Provide false if you need to perform more atomic actions.

  • mtyield

    should contain an address of the kernel routine: task_yield.

Expects:

  • hl

    an I/O buffer handle.

On return a CF flag always cleared and accumulator contains a number of characters ready to be read or a number of characters already written + nchars.

Modifies: af, preserves: hl and nchars.

# File lib/zxutils/multitasking_io.rb, line 413
def mtio_wait(action, nchars=1, disable_intr:true, enable_intr:true, mtyield:task_yield)
  raise ArgumentError, "nchars must not be nil" if nchars.nil?
  isolate do |eoc|
    check_ready     mtio_ready?(action, nchars:nchars, disable_intr:disable_intr, enable_intr:enable_intr)
                    jr   NC, eoc
                    call mtyield
                    di unless disable_intr
                    jr   check_ready
  end
end